From 4bc36bcc33d2e6e7f7efc1570442836152e39d71 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 12 Mar 2017 12:13:02 +0100 Subject: [PATCH 01/11] Convert LessCache to JavaScript --- src/less-cache.coffee | 214 -------------------------------- src/less-cache.js | 275 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 214 deletions(-) delete mode 100644 src/less-cache.coffee create mode 100644 src/less-cache.js diff --git a/src/less-cache.coffee b/src/less-cache.coffee deleted file mode 100644 index 60d7d79..0000000 --- a/src/less-cache.coffee +++ /dev/null @@ -1,214 +0,0 @@ -crypto = require 'crypto' -{basename, dirname, extname, join, relative} = require 'path' - -_ = require 'underscore-plus' -fs = require 'fs-plus' -less = null # Defer until it is actually used -lessFs = null # Defer until it is actually used -walkdir = require('walkdir').sync - -cacheVersion = 1 - -module.exports = -class LessCache - # Create a new Less cache with the given options. - # - # options - An object with following keys - # * cacheDir: A string path to the directory to store cached files in (required) - # - # * importPaths: An array of strings to configure the Less parser with (optional) - # - # * resourcePath: A string path to use for relativizing paths. This is useful if - # you want to make caches transferable between directories or - # machines. (optional) - # - # * fallbackDir: A string path to a directory containing a readable cache to read - # from an entry is not found in this cache (optional) - constructor: ({@cacheDir, @importPaths, @resourcePath, @fallbackDir, @syncCaches, @lessSourcesByRelativeFilePath}={}) -> - @lessSourcesByRelativeFilePath ?= {} - @importsCacheDir = @cacheDirectoryForImports(@importPaths) - if @fallbackDir - @importsFallbackDir = join(@fallbackDir, basename(@importsCacheDir)) - - try - {@importedFiles} = @readJson(join(@importsCacheDir, 'imports.json')) - - @setImportPaths(@importPaths) - - @stats = - hits: 0 - misses: 0 - - cacheDirectoryForImports: (importPaths=[]) -> - if @resourcePath - importPaths = importPaths.map (importPath) => - @relativize(@resourcePath, importPath) - join(@cacheDir, @digestForContent(importPaths.join('\n'))) - - getDirectory: -> @cacheDir - - getImportPaths: -> _.clone(@importPaths) - - getImportedFiles: (importPaths) -> - importedFiles = [] - for importPath in importPaths - try - walkdir importPath, no_return: true, (filePath, stat) => - return unless stat.isFile() - filePath = @relativize(@resourcePath, filePath) if @resourcePath - importedFiles.push(filePath) - catch error - continue - - importedFiles - - setImportPaths: (importPaths=[]) -> - importedFiles = @getImportedFiles(importPaths) - - pathsChanged = not _.isEqual(@importPaths, importPaths) - filesChanged = not _.isEqual(@importedFiles, importedFiles) - if pathsChanged - @importsCacheDir = @cacheDirectoryForImports(importPaths) - if @fallbackDir - @importsFallbackDir = join(@fallbackDir, basename(@importsCacheDir)) - else if filesChanged - try - fs.removeSync(@importsCacheDir) - catch error - if error?.code is 'ENOENT' - try - fs.removeSync(@importsCacheDir) # Retry once - - @writeJson(join(@importsCacheDir, 'imports.json'), {importedFiles}) - - @importedFiles = importedFiles - @importPaths = importPaths - - observeImportedFilePaths: (callback) -> - importedPaths = [] - lessFs ?= require 'less/lib/less-node/fs.js' - originalFsReadFileSync = lessFs.readFileSync - lessFs.readFileSync = (filePath, args...) => - relativeFilePath = @relativize(@resourcePath, filePath) if @resourcePath - content = @lessSourcesByRelativeFilePath[relativeFilePath] ? originalFsReadFileSync(filePath, args...) - importedPaths.push({path: relativeFilePath ? filePath, digest: @digestForContent(content)}) - content - - try - callback() - finally - lessFs.readFileSync = originalFsReadFileSync - - importedPaths - - readJson: (filePath) -> JSON.parse(fs.readFileSync(filePath)) - - writeJson: (filePath, object) -> fs.writeFileSync(filePath, JSON.stringify(object)) - - digestForPath: (relativeFilePath) -> - lessSource = @lessSourcesByRelativeFilePath[relativeFilePath] - unless lessSource? - absoluteFilePath = null - if @resourcePath and not fs.isAbsolute(relativeFilePath) - absoluteFilePath = join(@resourcePath, relativeFilePath) - else - absoluteFilePath = relativeFilePath - lessSource = fs.readFileSync(absoluteFilePath) - - @digestForContent(lessSource) - - digestForContent: (content) -> - crypto.createHash('SHA1').update(content, 'utf8').digest('hex') - - relativize: (from, to) -> - relativePath = relative(from, to) - if relativePath.indexOf('..') is 0 - to - else - relativePath - - getCachePath: (directory, filePath) -> - cacheFile = "#{basename(filePath, extname(filePath))}.json" - directoryPath = dirname(filePath) - directoryPath = @relativize(@resourcePath, directoryPath) if @resourcePath - directoryPath = @digestForContent(directoryPath) if directoryPath - join(directory, 'content', directoryPath, cacheFile) - - getCachedCss: (filePath, digest) -> - try - cacheEntry = @readJson(@getCachePath(@importsCacheDir, filePath)) - catch error - if @importsFallbackDir? - try - cacheEntry = @readJson(@getCachePath(@importsFallbackDir, filePath)) - fallbackDirUsed = true - - return unless digest is cacheEntry?.digest - - for {path, digest} in cacheEntry.imports - try - return if @digestForPath(path) isnt digest - catch error - return - - if @syncCaches - if fallbackDirUsed - @writeJson(@getCachePath(@importsCacheDir, filePath), cacheEntry) - else if @importsFallbackDir? - @writeJson(@getCachePath(@importsFallbackDir, filePath), cacheEntry) - - cacheEntry.css - - putCachedCss: (filePath, digest, css, imports) -> - cacheEntry = {digest, css, imports, version: cacheVersion} - @writeJson(@getCachePath(@importsCacheDir, filePath), cacheEntry) - - if @syncCaches and @importsFallbackDir? - @writeJson(@getCachePath(@importsFallbackDir, filePath), cacheEntry) - - parseLess: (filePath, contents) -> - css = null - options = filename: filePath, syncImport: true, paths: @importPaths - less ?= require('less') - imports = @observeImportedFilePaths -> - less.render contents, options, (error, result) -> - if error? - throw error - else - {css} = result - {imports, css} - - # Read the Less file at the current path and return either the cached CSS or the newly - # compiled CSS. This method caches the compiled CSS after it is generated. This cached - # CSS will be returned as long as the Less file and any of its imports are unchanged. - # - # filePath: A string path to a Less file. - # - # Returns the compiled CSS for the given path. - readFileSync: (absoluteFilePath) -> - fileContents = null - if @resourcePath and fs.isAbsolute(absoluteFilePath) - relativeFilePath = @relativize(@resourcePath, absoluteFilePath) - fileContents = @lessSourcesByRelativeFilePath[relativeFilePath] - - @cssForFile(absoluteFilePath, fileContents ? fs.readFileSync(absoluteFilePath, 'utf8')) - - # Return either cached CSS or the newly - # compiled CSS from `lessContent`. This method caches the compiled CSS after it is generated. This cached - # CSS will be returned as long as the Less file and any of its imports are unchanged. - # - # filePath: A string path to the Less file. - # lessContent: The contents of the filePath - # - # Returns the compiled CSS for the given path and lessContent - cssForFile: (filePath, lessContent) -> - digest = @digestForContent(lessContent) - css = @getCachedCss(filePath, digest) - if css? - @stats.hits++ - return css - - @stats.misses++ - {imports, css} = @parseLess(filePath, lessContent) - @putCachedCss(filePath, digest, css, imports) - css diff --git a/src/less-cache.js b/src/less-cache.js new file mode 100644 index 0000000..6148a7f --- /dev/null +++ b/src/less-cache.js @@ -0,0 +1,275 @@ +const crypto = require('crypto') +const {basename, dirname, extname, join, relative} = require('path') + +const _ = require('underscore-plus') +const fs = require('fs-plus') +let less = null // Defer until it is actually used +let lessFs = null // Defer until it is actually used +const walkdir = require('walkdir').sync + +const cacheVersion = 1 + +module.exports = +class LessCache { + // Create a new Less cache with the given options. + // + // options - An object with following keys + // * cacheDir: A string path to the directory to store cached files in (required) + // + // * importPaths: An array of strings to configure the Less parser with (optional) + // + // * resourcePath: A string path to use for relativizing paths. This is useful if + // you want to make caches transferable between directories or + // machines. (optional) + // + // * fallbackDir: A string path to a directory containing a readable cache to read + // from an entry is not found in this cache (optional) + constructor ({cacheDir, importPaths, resourcePath, fallbackDir, syncCaches, lessSourcesByRelativeFilePath} = {}) { + this.cacheDir = cacheDir + this.importPaths = importPaths + this.resourcePath = resourcePath + this.fallbackDir = fallbackDir + this.syncCaches = syncCaches + this.lessSourcesByRelativeFilePath = lessSourcesByRelativeFilePath + if (this.lessSourcesByRelativeFilePath == null) { this.lessSourcesByRelativeFilePath = {} } + this.importsCacheDir = this.cacheDirectoryForImports(this.importPaths) + if (this.fallbackDir) { + this.importsFallbackDir = join(this.fallbackDir, basename(this.importsCacheDir)) + } + + try { + ({importedFiles: this.importedFiles} = this.readJson(join(this.importsCacheDir, 'imports.json'))) + } catch (error) {} + + this.setImportPaths(this.importPaths) + + this.stats = { + hits: 0, + misses: 0 + } + } + + cacheDirectoryForImports (importPaths = []) { + if (this.resourcePath) { + importPaths = importPaths.map(importPath => { + return this.relativize(this.resourcePath, importPath) + } + ) + } + return join(this.cacheDir, this.digestForContent(importPaths.join('\n'))) + } + + getDirectory () { return this.cacheDir } + + getImportPaths () { return _.clone(this.importPaths) } + + getImportedFiles (importPaths) { + const importedFiles = [] + for (let importPath of importPaths) { + try { + walkdir(importPath, {no_return: true}, (filePath, stat) => { + if (!stat.isFile()) { return } + if (this.resourcePath) { filePath = this.relativize(this.resourcePath, filePath) } + return importedFiles.push(filePath) + } + ) + } catch (error) { + continue + } + } + + return importedFiles + } + + setImportPaths (importPaths = []) { + const importedFiles = this.getImportedFiles(importPaths) + + const pathsChanged = !_.isEqual(this.importPaths, importPaths) + const filesChanged = !_.isEqual(this.importedFiles, importedFiles) + if (pathsChanged) { + this.importsCacheDir = this.cacheDirectoryForImports(importPaths) + if (this.fallbackDir) { + this.importsFallbackDir = join(this.fallbackDir, basename(this.importsCacheDir)) + } + } else if (filesChanged) { + try { + fs.removeSync(this.importsCacheDir) + } catch (error) { + if (error && error.code === 'ENOENT') { + try { + fs.removeSync(this.importsCacheDir) // Retry once + } catch (error) {} + } + } + } + + this.writeJson(join(this.importsCacheDir, 'imports.json'), {importedFiles}) + + this.importedFiles = importedFiles + this.importPaths = importPaths + } + + observeImportedFilePaths (callback) { + const importedPaths = [] + if (lessFs == null) { lessFs = require('less/lib/less-node/fs.js') } + const originalFsReadFileSync = lessFs.readFileSync + lessFs.readFileSync = (filePath, ...args) => { + let relativeFilePath + if (this.resourcePath) { relativeFilePath = this.relativize(this.resourcePath, filePath) } + const content = this.lessSourcesByRelativeFilePath[relativeFilePath] != null ? this.lessSourcesByRelativeFilePath[relativeFilePath] : originalFsReadFileSync(filePath, ...args) + importedPaths.push({path: relativeFilePath != null ? relativeFilePath : filePath, digest: this.digestForContent(content)}) + return content + } + + try { + callback() + } finally { + lessFs.readFileSync = originalFsReadFileSync + } + + return importedPaths + } + + readJson (filePath) { return JSON.parse(fs.readFileSync(filePath)) } + + writeJson (filePath, object) { return fs.writeFileSync(filePath, JSON.stringify(object)) } + + digestForPath (relativeFilePath) { + let lessSource = this.lessSourcesByRelativeFilePath[relativeFilePath] + if (lessSource == null) { + let absoluteFilePath = null + if (this.resourcePath && !fs.isAbsolute(relativeFilePath)) { + absoluteFilePath = join(this.resourcePath, relativeFilePath) + } else { + absoluteFilePath = relativeFilePath + } + lessSource = fs.readFileSync(absoluteFilePath) + } + + return this.digestForContent(lessSource) + } + + digestForContent (content) { + return crypto.createHash('SHA1').update(content, 'utf8').digest('hex') + } + + relativize (from, to) { + const relativePath = relative(from, to) + if (relativePath.indexOf('..') === 0) { + return to + } else { + return relativePath + } + } + + getCachePath (directory, filePath) { + const cacheFile = `${basename(filePath, extname(filePath))}.json` + let directoryPath = dirname(filePath) + if (this.resourcePath) { directoryPath = this.relativize(this.resourcePath, directoryPath) } + if (directoryPath) { directoryPath = this.digestForContent(directoryPath) } + return join(directory, 'content', directoryPath, cacheFile) + } + + getCachedCss (filePath, digest) { + let cacheEntry, fallbackDirUsed, path + try { + cacheEntry = this.readJson(this.getCachePath(this.importsCacheDir, filePath)) + } catch (error) { + if (this.importsFallbackDir != null) { + try { + cacheEntry = this.readJson(this.getCachePath(this.importsFallbackDir, filePath)) + fallbackDirUsed = true + } catch (error) {} + } + } + + if (!cacheEntry || digest !== cacheEntry.digest) { + return + } + + for ({path, digest} of cacheEntry.imports) { + try { + if (this.digestForPath(path) !== digest) { + return + } + } catch (error) { + return + } + } + + if (this.syncCaches) { + if (fallbackDirUsed) { + this.writeJson(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) + } else if (this.importsFallbackDir != null) { + this.writeJson(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) + } + } + + return cacheEntry.css + } + + putCachedCss (filePath, digest, css, imports) { + const cacheEntry = {digest, css, imports, version: cacheVersion} + this.writeJson(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) + + if (this.syncCaches && (this.importsFallbackDir != null)) { + return this.writeJson(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) + } + } + + parseLess (filePath, contents) { + let css = null + const options = {filename: filePath, syncImport: true, paths: this.importPaths} + if (less == null) { less = require('less') } + const imports = this.observeImportedFilePaths(() => + less.render(contents, options, function (error, result) { + if (error != null) { + throw error + } else { + css = result.css + } + }) + ) + return {imports, css} + } + + // Read the Less file at the current path and return either the cached CSS or the newly + // compiled CSS. This method caches the compiled CSS after it is generated. This cached + // CSS will be returned as long as the Less file and any of its imports are unchanged. + // + // filePath: A string path to a Less file. + // + // Returns the compiled CSS for the given path. + readFileSync (absoluteFilePath) { + let fileContents = null + if (this.resourcePath && fs.isAbsolute(absoluteFilePath)) { + const relativeFilePath = this.relativize(this.resourcePath, absoluteFilePath) + fileContents = this.lessSourcesByRelativeFilePath[relativeFilePath] + } + + return this.cssForFile(absoluteFilePath, fileContents != null ? fileContents : fs.readFileSync(absoluteFilePath, 'utf8')) + } + + // Return either cached CSS or the newly + // compiled CSS from `lessContent`. This method caches the compiled CSS after it is generated. This cached + // CSS will be returned as long as the Less file and any of its imports are unchanged. + // + // filePath: A string path to the Less file. + // lessContent: The contents of the filePath + // + // Returns the compiled CSS for the given path and lessContent + cssForFile (filePath, lessContent) { + let imports + const digest = this.digestForContent(lessContent) + let css = this.getCachedCss(filePath, digest) + if (css != null) { + this.stats.hits++ + return css + } + + this.stats.misses++ + ({imports, css} = this.parseLess(filePath, lessContent)) + this.putCachedCss(filePath, digest, css, imports) + return css + } +} From 51524b1be732a508115e0546ed2ea3c1334cf256 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 12 Mar 2017 12:21:57 +0100 Subject: [PATCH 02/11] Convert specs to JavaScript --- package.json | 1 + spec/less-cache-spec.coffee | 361 ---------------------------------- spec/less-cache-spec.js | 382 ++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+), 361 deletions(-) delete mode 100644 spec/less-cache-spec.coffee create mode 100644 spec/less-cache-spec.js diff --git a/package.json b/package.json index 3dfd0c3..7b7b6e7 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "walkdir": "0.0.11" }, "devDependencies": { + "dedent": "^0.7.0", "fstream": "^1.0.10", "grunt": "^1.0.1", "grunt-cli": "^1.2.0", diff --git a/spec/less-cache-spec.coffee b/spec/less-cache-spec.coffee deleted file mode 100644 index 35df10a..0000000 --- a/spec/less-cache-spec.coffee +++ /dev/null @@ -1,361 +0,0 @@ -fs = require 'fs' -{dirname, join} = require 'path' - -tmp = require 'tmp' -temp = require('temp').track() -fstream = require 'fstream' - -LessCache = require '../src/less-cache' - -describe "LessCache", -> - [cache, fixturesDir] = [] - - beforeEach -> - fixturesDir = null - tmp.dir (error, tempDir) -> - reader = fstream.Reader(join(__dirname, 'fixtures')) - reader.on 'end', -> - fixturesDir = tempDir - cacheConfig = - importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')] - cacheDir: join(tempDir, 'cache') - cache = new LessCache(cacheConfig) - reader.pipe(fstream.Writer(tempDir)) - - waitsFor -> fixturesDir? - - describe "::cssForFile(filePath)", -> - filePath = null - fileLess = """ - @import "a"; - @import "b"; - @import "c"; - @import "d"; - - body { - a: @a; - b: @b; - c: @c; - d: @d; - } - """ - - beforeEach -> - filePath = join(fixturesDir, 'imports.less') - - it "returns the compiled CSS for a given path and Less content", -> - css = cache.cssForFile(filePath, fileLess) - expect(css).toBe """ - body { - a: 1; - b: 2; - c: 3; - d: 4; - } - - """ - - describe "::readFileSync(filePath)", -> - [css] = [] - - beforeEach -> - css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache.stats.hits).toBe 0 - expect(cache.stats.misses).toBe 1 - - it "returns the compiled CSS for a given Less file path", -> - expect(css).toBe """ - body { - a: 1; - b: 2; - c: 3; - d: 4; - } - - """ - - it "returns the cached CSS for a given Less file path", -> - spyOn(cache, 'parseLess').andCallThrough() - expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).toBe """ - body { - a: 1; - b: 2; - c: 3; - d: 4; - } - - """ - expect(cache.parseLess.callCount).toBe 0 - expect(cache.stats.hits).toBe 1 - expect(cache.stats.misses).toBe 1 - - it "reflects changes to the file being read", -> - fs.writeFileSync(join(fixturesDir, 'imports.less'), 'body { display: block; }') - css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe """ - body { - display: block; - } - - """ - - it "reflects changes to files imported by the file being read", -> - fs.writeFileSync(join(fixturesDir, 'b.less'), '@b: 20;') - css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe """ - body { - a: 1; - b: 20; - c: 3; - d: 4; - } - - """ - - it "reflects changes to files on the import path", -> - fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 40;') - cache.setImportPaths(cache.getImportPaths()) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe """ - body { - a: 1; - b: 2; - c: 3; - d: 40; - } - - """ - - fs.unlinkSync(join(fixturesDir, 'imports-1', 'c.less')) - cache.setImportPaths(cache.getImportPaths()) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe """ - body { - a: 1; - b: 2; - c: 30; - d: 40; - } - - """ - - fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 400;') - cache.setImportPaths(cache.getImportPaths()) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe """ - body { - a: 1; - b: 2; - c: 30; - d: 400; - } - - """ - - it "reflect changes to the import paths array", -> - spyOn(cache, 'parseLess').andCallThrough() - cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) - cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache.parseLess.callCount).toBe 0 - - cache.setImportPaths([join(fixturesDir, 'imports-2'), join(fixturesDir, 'imports-1'), join(fixturesDir, 'import-does-not-exist')]) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe """ - body { - a: 1; - b: 2; - c: 30; - d: 4; - } - - """ - expect(cache.parseLess.callCount).toBe 1 - - cache.parseLess.reset() - cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) - expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).toBe """ - body { - a: 1; - b: 2; - c: 3; - d: 4; - } - - """ - expect(cache.parseLess.callCount).toBe 0 - - it "reuses cached CSS across cache instances", -> - cache2 = new LessCache(cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths()) - spyOn(cache2, 'parseLess').andCallThrough() - cache2.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache2.parseLess.callCount).toBe 0 - - it "throws compile errors", -> - expect(-> cache.readFileSync(join(fixturesDir, 'invalid.less'))).toThrow() - - it "throws file not found errors", -> - expect(-> cache.readFileSync(join(fixturesDir, 'does-not-exist.less'))).toThrow() - - it "relativizes cache paths based on the configured resource path", -> - cache2 = new LessCache(cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths(), resourcePath: fixturesDir) - expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).toBeFalsy() - cache2.readFileSync(join(fixturesDir, 'imports.less')) - expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).toBeTruthy() - - it "uses the fallback directory when no cache entry is found in the primary directory", -> - cache2 = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'cache2') - importPaths: cache.getImportPaths() - fallbackDir: cache.getDirectory() - resourcePath: fixturesDir - cache2.readFileSync(join(fixturesDir, 'imports.less')) - - cache3 = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'cache3') - importPaths: cache2.getImportPaths() - fallbackDir: cache2.getDirectory() - resourcePath: fixturesDir - - spyOn(cache3, 'parseLess').andCallThrough() - cache3.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache3.parseLess.callCount).toBe 0 - - describe "when syncCaches option is set to true", -> - it "writes the cache entry to the fallback cache when initially uncached", -> - fallback = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'fallback') - resourcePath: fixturesDir - - cache = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'synced') - syncCaches: true - fallbackDir: join(dirname(cache.getDirectory()), 'fallback') - resourcePath: fixturesDir - - cacheCss = cache.readFileSync(join(fixturesDir, 'a.less')) - expect(cache.stats.hits).toBe 0 - expect(cache.stats.misses).toBe 1 - - fallbackCss = fallback.readFileSync(join(fixturesDir, 'a.less')) - expect(fallback.stats.hits).toBe 1 - expect(fallback.stats.misses).toBe 0 - - expect(cacheCss).toBe fallbackCss - - it "writes the cache entry to the fallback cache when read from the main cache", -> - cache = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'synced') - resourcePath: fixturesDir - - fallback = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'fallback') - resourcePath: fixturesDir - - cacheWithFallback = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'synced') - syncCaches: true - fallbackDir: join(dirname(cache.getDirectory()), 'fallback') - resourcePath: fixturesDir - - # Prime main cache - cache.readFileSync(join(fixturesDir, 'a.less')) - - # Read from main cache with write to fallback - cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) - - # Read from fallback cache - fallback.readFileSync(join(fixturesDir, 'a.less')) - - expect(fallback.stats.hits).toBe 1 - expect(fallback.stats.misses).toBe 0 - - it "writes the cache entry to the main cache when read from the fallback cache", -> - cache = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'synced') - resourcePath: fixturesDir - - fallback = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'fallback') - resourcePath: fixturesDir - - cacheWithFallback = new LessCache - cacheDir: join(dirname(cache.getDirectory()), 'synced') - syncCaches: true - fallbackDir: join(dirname(cache.getDirectory()), 'fallback') - resourcePath: fixturesDir - - # Prime fallback cache - fallback.readFileSync(join(fixturesDir, 'a.less')) - - # Read from fallback with write to main cache - cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) - - # Read from main cache - cache.readFileSync(join(fixturesDir, 'a.less')) - - expect(cache.stats.hits).toBe 1 - expect(cache.stats.misses).toBe 0 - - describe "when providing a resource path and less sources by relative file path", -> - it "reads from the provided sources first, and falls back to reading from disk if a valid source isn't available", -> - cacheDir = temp.mkdirSync() - cache1 = new LessCache - cacheDir: cacheDir - importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')] - resourcePath: fixturesDir - lessSourcesByRelativeFilePath: { - 'imports.less': """ - @import "a"; - @import "b"; - @import "c"; - @import "d"; - - some-selector { - prop-1: @a; - prop-2: @b; - prop-3: @c; - prop-4: @d; - } - """ - } - - expect(cache1.readFileSync(join(fixturesDir, 'imports.less'))).toBe(""" - some-selector { - prop-1: 1; - prop-2: 2; - prop-3: 3; - prop-4: 4; - }\n - """) - - cache2 = new LessCache - cacheDir: cacheDir - importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')] - resourcePath: fixturesDir - lessSourcesByRelativeFilePath: { - 'imports.less': """ - @import "a"; - @import "b"; - @import "c"; - @import "d"; - - some-selector { - prop-1: @a; - prop-2: @b; - prop-3: @c; - prop-4: @d; - } - """, - 'imports-1/c.less': """ - @c: "changed"; - """ - } - - expect(cache2.readFileSync(join(fixturesDir, 'imports.less'))).toBe(""" - some-selector { - prop-1: 1; - prop-2: 2; - prop-3: "changed"; - prop-4: 4; - }\n - """) diff --git a/spec/less-cache-spec.js b/spec/less-cache-spec.js new file mode 100644 index 0000000..19784e7 --- /dev/null +++ b/spec/less-cache-spec.js @@ -0,0 +1,382 @@ +const fs = require('fs') +const {dirname, join} = require('path') + +const tmp = require('tmp') +const temp = require('temp').track() +const fstream = require('fstream') +const dedent = require('dedent') + +const LessCache = require('../src/less-cache') + +describe('LessCache', function () { + let [cache, fixturesDir] = [] + + beforeEach(function () { + fixturesDir = null + tmp.dir(function (error, tempDir) { + const reader = fstream.Reader(join(__dirname, 'fixtures')) + reader.on('end', function () { + fixturesDir = tempDir + const cacheConfig = { + importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')], + cacheDir: join(tempDir, 'cache') + } + return cache = new LessCache(cacheConfig) + }) + return reader.pipe(fstream.Writer(tempDir)) + }) + + return waitsFor(() => fixturesDir != null) + }) + + describe('::cssForFile(filePath)', function () { + let filePath = null + const fileLess = dedent` + @import "a"; + @import "b"; + @import "c"; + @import "d"; + + body { + a: @a; + b: @b; + c: @c; + d: @d; + } + ` + + beforeEach(() => filePath = join(fixturesDir, 'imports.less')) + + return it('returns the compiled CSS for a given path and Less content', function () { + const css = cache.cssForFile(filePath, fileLess) + expect(css).toBe(dedent` + body { + a: 1; + b: 2; + c: 3; + d: 4; + }\n + `) + }) + }) + + describe('::readFileSync(filePath)', function () { + let [css] = [] + + beforeEach(function () { + css = cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(cache.stats.hits).toBe(0) + expect(cache.stats.misses).toBe(1) + }) + + it('returns the compiled CSS for a given Less file path', () => + expect(css).toBe(dedent` + body { + a: 1; + b: 2; + c: 3; + d: 4; + }\n + `) + ) + + it('returns the cached CSS for a given Less file path', function () { + spyOn(cache, 'parseLess').andCallThrough() + expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + body { + a: 1; + b: 2; + c: 3; + d: 4; + }\n + `) + expect(cache.parseLess.callCount).toBe(0) + expect(cache.stats.hits).toBe(1) + expect(cache.stats.misses).toBe(1) + }) + + it('reflects changes to the file being read', function () { + fs.writeFileSync(join(fixturesDir, 'imports.less'), 'body { display: block; }') + css = cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(css).toBe(dedent` + body { + display: block; + }\n + `) + }) + + it('reflects changes to files imported by the file being read', function () { + fs.writeFileSync(join(fixturesDir, 'b.less'), '@b: 20;') + css = cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(css).toBe(dedent` + body { + a: 1; + b: 20; + c: 3; + d: 4; + }\n + `) + }) + + it('reflects changes to files on the import path', function () { + fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 40;') + cache.setImportPaths(cache.getImportPaths()) + css = cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(css).toBe(dedent` + body { + a: 1; + b: 2; + c: 3; + d: 40; + }\n + `) + + fs.unlinkSync(join(fixturesDir, 'imports-1', 'c.less')) + cache.setImportPaths(cache.getImportPaths()) + css = cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(css).toBe(dedent` + body { + a: 1; + b: 2; + c: 30; + d: 40; + }\n + `) + + fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 400;') + cache.setImportPaths(cache.getImportPaths()) + css = cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(css).toBe(dedent` + body { + a: 1; + b: 2; + c: 30; + d: 400; + }\n + `) + }) + + it('reflect changes to the import paths array', function () { + spyOn(cache, 'parseLess').andCallThrough() + cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) + cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(cache.parseLess.callCount).toBe(0) + + cache.setImportPaths([join(fixturesDir, 'imports-2'), join(fixturesDir, 'imports-1'), join(fixturesDir, 'import-does-not-exist')]) + css = cache.readFileSync(join(fixturesDir, 'imports.less')) + expect(css).toBe(dedent` + body { + a: 1; + b: 2; + c: 30; + d: 4; + }\n + `) + expect(cache.parseLess.callCount).toBe(1) + + cache.parseLess.reset() + cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) + expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + body { + a: 1; + b: 2; + c: 3; + d: 4; + }\n + `) + expect(cache.parseLess.callCount).toBe(0) + }) + + it('reuses cached CSS across cache instances', function () { + const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths()}) + spyOn(cache2, 'parseLess').andCallThrough() + cache2.readFileSync(join(fixturesDir, 'imports.less')) + expect(cache2.parseLess.callCount).toBe(0) + }) + + it('throws compile errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'invalid.less'))).toThrow()) + + it('throws file not found errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'does-not-exist.less'))).toThrow()) + + it('relativizes cache paths based on the configured resource path', function () { + const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths(), resourcePath: fixturesDir}) + expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).toBeFalsy() + cache2.readFileSync(join(fixturesDir, 'imports.less')) + expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).toBeTruthy() + }) + + return it('uses the fallback directory when no cache entry is found in the primary directory', function () { + const cache2 = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'cache2'), + importPaths: cache.getImportPaths(), + fallbackDir: cache.getDirectory(), + resourcePath: fixturesDir + }) + cache2.readFileSync(join(fixturesDir, 'imports.less')) + + const cache3 = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'cache3'), + importPaths: cache2.getImportPaths(), + fallbackDir: cache2.getDirectory(), + resourcePath: fixturesDir + }) + + spyOn(cache3, 'parseLess').andCallThrough() + cache3.readFileSync(join(fixturesDir, 'imports.less')) + expect(cache3.parseLess.callCount).toBe(0) + }) + }) + + describe('when syncCaches option is set to true', function () { + it('writes the cache entry to the fallback cache when initially uncached', function () { + const fallback = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'fallback'), + resourcePath: fixturesDir + }) + + cache = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'synced'), + syncCaches: true, + fallbackDir: join(dirname(cache.getDirectory()), 'fallback'), + resourcePath: fixturesDir + }) + + const cacheCss = cache.readFileSync(join(fixturesDir, 'a.less')) + expect(cache.stats.hits).toBe(0) + expect(cache.stats.misses).toBe(1) + + const fallbackCss = fallback.readFileSync(join(fixturesDir, 'a.less')) + expect(fallback.stats.hits).toBe(1) + expect(fallback.stats.misses).toBe(0) + + expect(cacheCss).toBe(fallbackCss) + }) + + it('writes the cache entry to the fallback cache when read from the main cache', function () { + cache = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'synced'), + resourcePath: fixturesDir + }) + + const fallback = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'fallback'), + resourcePath: fixturesDir + }) + + const cacheWithFallback = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'synced'), + syncCaches: true, + fallbackDir: join(dirname(cache.getDirectory()), 'fallback'), + resourcePath: fixturesDir + }) + + // Prime main cache + cache.readFileSync(join(fixturesDir, 'a.less')) + + // Read from main cache with write to fallback + cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) + + // Read from fallback cache + fallback.readFileSync(join(fixturesDir, 'a.less')) + + expect(fallback.stats.hits).toBe(1) + expect(fallback.stats.misses).toBe(0) + }) + + return it('writes the cache entry to the main cache when read from the fallback cache', function () { + cache = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'synced'), + resourcePath: fixturesDir + }) + + const fallback = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'fallback'), + resourcePath: fixturesDir + }) + + const cacheWithFallback = new LessCache({ + cacheDir: join(dirname(cache.getDirectory()), 'synced'), + syncCaches: true, + fallbackDir: join(dirname(cache.getDirectory()), 'fallback'), + resourcePath: fixturesDir + }) + + // Prime fallback cache + fallback.readFileSync(join(fixturesDir, 'a.less')) + + // Read from fallback with write to main cache + cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) + + // Read from main cache + cache.readFileSync(join(fixturesDir, 'a.less')) + + expect(cache.stats.hits).toBe(1) + expect(cache.stats.misses).toBe(0) + }) + }) + + return describe('when providing a resource path and less sources by relative file path', () => + it("reads from the provided sources first, and falls back to reading from disk if a valid source isn't available", function () { + const cacheDir = temp.mkdirSync() + const cache1 = new LessCache({ + cacheDir, + importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')], + resourcePath: fixturesDir, + lessSourcesByRelativeFilePath: { + 'imports.less': dedent` + @import "a"; + @import "b"; + @import "c"; + @import "d"; + + some-selector { + prop-1: @a; + prop-2: @b; + prop-3: @c; + prop-4: @d; + }\n + ` + } + }) + + expect(cache1.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + some-selector { + prop-1: 1; + prop-2: 2; + prop-3: 3; + prop-4: 4; + }\n + `) + + const cache2 = new LessCache({ + cacheDir, + importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')], + resourcePath: fixturesDir, + lessSourcesByRelativeFilePath: { + 'imports.less': dedent` + @import "a"; + @import "b"; + @import "c"; + @import "d"; + + some-selector { + prop-1: @a; + prop-2: @b; + prop-3: @c; + prop-4: @d; + }\n + `, + 'imports-1/c.less': '@c: "changed";\n' + }}) + + expect(cache2.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + some-selector { + prop-1: 1; + prop-2: 2; + prop-3: "changed"; + prop-4: 4; + }\n + `) + }) + ) +}) From 97ca4e63d30de77cfd0d6617bc69459b8b26bdb6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 12 Mar 2017 13:56:32 +0100 Subject: [PATCH 03/11] Use mocha --- .babelrc | 5 + .npmignore | 2 +- Gruntfile.coffee | 38 ----- package.json | 18 ++- {spec => test}/fixtures/a.less | 0 {spec => test}/fixtures/b.less | 0 {spec => test}/fixtures/imports-1/c.less | 0 {spec => test}/fixtures/imports-2/c.less | 0 {spec => test}/fixtures/imports-2/d.less | 0 {spec => test}/fixtures/imports.less | 0 {spec => test}/fixtures/invalid.less | 0 .../less-cache.test.js | 141 ++++++++---------- 12 files changed, 81 insertions(+), 123 deletions(-) create mode 100644 .babelrc delete mode 100644 Gruntfile.coffee rename {spec => test}/fixtures/a.less (100%) rename {spec => test}/fixtures/b.less (100%) rename {spec => test}/fixtures/imports-1/c.less (100%) rename {spec => test}/fixtures/imports-2/c.less (100%) rename {spec => test}/fixtures/imports-2/d.less (100%) rename {spec => test}/fixtures/imports.less (100%) rename {spec => test}/fixtures/invalid.less (100%) rename spec/less-cache-spec.js => test/less-cache.test.js (76%) diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..84679ca --- /dev/null +++ b/.babelrc @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-async-to-generator" + ] +} diff --git a/.npmignore b/.npmignore index 63cf84a..e349110 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,4 @@ -*.coffee +src spec .pairs .travis.yml diff --git a/Gruntfile.coffee b/Gruntfile.coffee deleted file mode 100644 index e01d66d..0000000 --- a/Gruntfile.coffee +++ /dev/null @@ -1,38 +0,0 @@ -module.exports = (grunt) -> - grunt.initConfig - pkg: grunt.file.readJSON('package.json') - - coffee: - glob_to_multiple: - expand: true - cwd: 'src' - src: ['*.coffee'] - dest: 'lib' - ext: '.js' - - coffeelint: - options: - no_empty_param_list: - level: 'error' - max_line_length: - level: 'ignore' - - src: ['src/*.coffee'] - test: ['spec/*.coffee'] - - shell: - test: - command: 'node node_modules/jasmine-focused/bin/jasmine-focused --captureExceptions --coffee spec' - options: - stdout: true - stderr: true - failOnError: true - - grunt.loadNpmTasks('grunt-contrib-coffee') - grunt.loadNpmTasks('grunt-shell') - grunt.loadNpmTasks('grunt-coffeelint') - - grunt.registerTask 'clean', -> require('fs-plus').removeSync('lib') - grunt.registerTask('lint', ['coffeelint:src', 'coffeelint:test']) - grunt.registerTask('default', ['coffeelint', 'coffee']) - grunt.registerTask('test', ['default', 'coffeelint:test', 'shell:test']) diff --git a/package.json b/package.json index 7b7b6e7..5d0374f 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "Less compile cache", "main": "./lib/less-cache", "scripts": { - "prepublish": "grunt clean lint coffee", - "test": "grunt test" + "test": "mocha test/**/*.test.js --compilers js:babel-register", + "prepublish": "rimraf lib && babel src -d lib" }, "repository": { "type": "git", @@ -33,14 +33,16 @@ "walkdir": "0.0.11" }, "devDependencies": { + "babel-cli": "^6.23.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-register": "^6.23.0", "dedent": "^0.7.0", + "expect.js": "^0.3.1", + "fs-plus": "^3.0.0", "fstream": "^1.0.10", - "grunt": "^1.0.1", - "grunt-cli": "^1.2.0", - "grunt-coffeelint": "0.0.16", - "grunt-contrib-coffee": "^1.0.0", - "grunt-shell": "^1.3.0", - "jasmine-focused": "1.x", + "mocha": "^3.2.0", + "rimraf": "^2.6.1", + "sinon": "^1.17.7", "temp": "^0.8.3", "tmp": "0.0.28" } diff --git a/spec/fixtures/a.less b/test/fixtures/a.less similarity index 100% rename from spec/fixtures/a.less rename to test/fixtures/a.less diff --git a/spec/fixtures/b.less b/test/fixtures/b.less similarity index 100% rename from spec/fixtures/b.less rename to test/fixtures/b.less diff --git a/spec/fixtures/imports-1/c.less b/test/fixtures/imports-1/c.less similarity index 100% rename from spec/fixtures/imports-1/c.less rename to test/fixtures/imports-1/c.less diff --git a/spec/fixtures/imports-2/c.less b/test/fixtures/imports-2/c.less similarity index 100% rename from spec/fixtures/imports-2/c.less rename to test/fixtures/imports-2/c.less diff --git a/spec/fixtures/imports-2/d.less b/test/fixtures/imports-2/d.less similarity index 100% rename from spec/fixtures/imports-2/d.less rename to test/fixtures/imports-2/d.less diff --git a/spec/fixtures/imports.less b/test/fixtures/imports.less similarity index 100% rename from spec/fixtures/imports.less rename to test/fixtures/imports.less diff --git a/spec/fixtures/invalid.less b/test/fixtures/invalid.less similarity index 100% rename from spec/fixtures/invalid.less rename to test/fixtures/invalid.less diff --git a/spec/less-cache-spec.js b/test/less-cache.test.js similarity index 76% rename from spec/less-cache-spec.js rename to test/less-cache.test.js index 19784e7..684d466 100644 --- a/spec/less-cache-spec.js +++ b/test/less-cache.test.js @@ -1,7 +1,9 @@ -const fs = require('fs') +const expect = require('expect.js') +const sinon = require('sinon') + +const fs = require('fs-plus') const {dirname, join} = require('path') -const tmp = require('tmp') const temp = require('temp').track() const fstream = require('fstream') const dedent = require('dedent') @@ -12,44 +14,31 @@ describe('LessCache', function () { let [cache, fixturesDir] = [] beforeEach(function () { - fixturesDir = null - tmp.dir(function (error, tempDir) { - const reader = fstream.Reader(join(__dirname, 'fixtures')) - reader.on('end', function () { - fixturesDir = tempDir - const cacheConfig = { - importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')], - cacheDir: join(tempDir, 'cache') - } - return cache = new LessCache(cacheConfig) - }) - return reader.pipe(fstream.Writer(tempDir)) + fixturesDir = temp.path() + fs.copySync(join(__dirname, 'fixtures'), fixturesDir) + cache = new LessCache({ + importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')], + cacheDir: join(fixturesDir, 'cache') }) - - return waitsFor(() => fixturesDir != null) }) describe('::cssForFile(filePath)', function () { - let filePath = null - const fileLess = dedent` - @import "a"; - @import "b"; - @import "c"; - @import "d"; - - body { - a: @a; - b: @b; - c: @c; - d: @d; - } - ` - - beforeEach(() => filePath = join(fixturesDir, 'imports.less')) - - return it('returns the compiled CSS for a given path and Less content', function () { - const css = cache.cssForFile(filePath, fileLess) - expect(css).toBe(dedent` + it('returns the compiled CSS for a given path and Less content', function () { + const fileLess = dedent` + @import "a"; + @import "b"; + @import "c"; + @import "d"; + + body { + a: @a; + b: @b; + c: @c; + d: @d; + } + ` + const css = cache.cssForFile(join(fixturesDir, 'imports.less'), fileLess) + expect(css).to.be(dedent` body { a: 1; b: 2; @@ -65,12 +54,12 @@ describe('LessCache', function () { beforeEach(function () { css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache.stats.hits).toBe(0) - expect(cache.stats.misses).toBe(1) + expect(cache.stats.hits).to.be(0) + expect(cache.stats.misses).to.be(1) }) it('returns the compiled CSS for a given Less file path', () => - expect(css).toBe(dedent` + expect(css).to.be(dedent` body { a: 1; b: 2; @@ -81,8 +70,8 @@ describe('LessCache', function () { ) it('returns the cached CSS for a given Less file path', function () { - spyOn(cache, 'parseLess').andCallThrough() - expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + sinon.spy(cache, 'parseLess') + expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` body { a: 1; b: 2; @@ -90,15 +79,15 @@ describe('LessCache', function () { d: 4; }\n `) - expect(cache.parseLess.callCount).toBe(0) - expect(cache.stats.hits).toBe(1) - expect(cache.stats.misses).toBe(1) + expect(cache.parseLess.callCount).to.be(0) + expect(cache.stats.hits).to.be(1) + expect(cache.stats.misses).to.be(1) }) it('reflects changes to the file being read', function () { fs.writeFileSync(join(fixturesDir, 'imports.less'), 'body { display: block; }') css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe(dedent` + expect(css).to.be(dedent` body { display: block; }\n @@ -108,7 +97,7 @@ describe('LessCache', function () { it('reflects changes to files imported by the file being read', function () { fs.writeFileSync(join(fixturesDir, 'b.less'), '@b: 20;') css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe(dedent` + expect(css).to.be(dedent` body { a: 1; b: 20; @@ -122,7 +111,7 @@ describe('LessCache', function () { fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 40;') cache.setImportPaths(cache.getImportPaths()) css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe(dedent` + expect(css).to.be(dedent` body { a: 1; b: 2; @@ -134,7 +123,7 @@ describe('LessCache', function () { fs.unlinkSync(join(fixturesDir, 'imports-1', 'c.less')) cache.setImportPaths(cache.getImportPaths()) css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe(dedent` + expect(css).to.be(dedent` body { a: 1; b: 2; @@ -146,7 +135,7 @@ describe('LessCache', function () { fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 400;') cache.setImportPaths(cache.getImportPaths()) css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe(dedent` + expect(css).to.be(dedent` body { a: 1; b: 2; @@ -157,14 +146,14 @@ describe('LessCache', function () { }) it('reflect changes to the import paths array', function () { - spyOn(cache, 'parseLess').andCallThrough() + sinon.spy(cache, 'parseLess') cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache.parseLess.callCount).toBe(0) + expect(cache.parseLess.callCount).to.be(0) cache.setImportPaths([join(fixturesDir, 'imports-2'), join(fixturesDir, 'imports-1'), join(fixturesDir, 'import-does-not-exist')]) css = cache.readFileSync(join(fixturesDir, 'imports.less')) - expect(css).toBe(dedent` + expect(css).to.be(dedent` body { a: 1; b: 2; @@ -172,11 +161,11 @@ describe('LessCache', function () { d: 4; }\n `) - expect(cache.parseLess.callCount).toBe(1) + expect(cache.parseLess.callCount).to.be(1) cache.parseLess.reset() cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) - expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` body { a: 1; b: 2; @@ -184,28 +173,28 @@ describe('LessCache', function () { d: 4; }\n `) - expect(cache.parseLess.callCount).toBe(0) + expect(cache.parseLess.callCount).to.be(0) }) it('reuses cached CSS across cache instances', function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths()}) - spyOn(cache2, 'parseLess').andCallThrough() + sinon.spy(cache2, 'parseLess') cache2.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache2.parseLess.callCount).toBe(0) + expect(cache2.parseLess.callCount).to.be(0) }) - it('throws compile errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'invalid.less'))).toThrow()) + it('throws compile errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'invalid.less'))).to.throwError()) - it('throws file not found errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'does-not-exist.less'))).toThrow()) + it('throws file not found errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'does-not-exist.less'))).to.throwError()) it('relativizes cache paths based on the configured resource path', function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths(), resourcePath: fixturesDir}) - expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).toBeFalsy() + expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(false) cache2.readFileSync(join(fixturesDir, 'imports.less')) - expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).toBeTruthy() + expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(true) }) - return it('uses the fallback directory when no cache entry is found in the primary directory', function () { + it('uses the fallback directory when no cache entry is found in the primary directory', function () { const cache2 = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'cache2'), importPaths: cache.getImportPaths(), @@ -221,9 +210,9 @@ describe('LessCache', function () { resourcePath: fixturesDir }) - spyOn(cache3, 'parseLess').andCallThrough() + sinon.spy(cache3, 'parseLess') cache3.readFileSync(join(fixturesDir, 'imports.less')) - expect(cache3.parseLess.callCount).toBe(0) + expect(cache3.parseLess.callCount).to.be(0) }) }) @@ -242,14 +231,14 @@ describe('LessCache', function () { }) const cacheCss = cache.readFileSync(join(fixturesDir, 'a.less')) - expect(cache.stats.hits).toBe(0) - expect(cache.stats.misses).toBe(1) + expect(cache.stats.hits).to.be(0) + expect(cache.stats.misses).to.be(1) const fallbackCss = fallback.readFileSync(join(fixturesDir, 'a.less')) - expect(fallback.stats.hits).toBe(1) - expect(fallback.stats.misses).toBe(0) + expect(fallback.stats.hits).to.be(1) + expect(fallback.stats.misses).to.be(0) - expect(cacheCss).toBe(fallbackCss) + expect(cacheCss).to.be(fallbackCss) }) it('writes the cache entry to the fallback cache when read from the main cache', function () { @@ -279,11 +268,11 @@ describe('LessCache', function () { // Read from fallback cache fallback.readFileSync(join(fixturesDir, 'a.less')) - expect(fallback.stats.hits).toBe(1) - expect(fallback.stats.misses).toBe(0) + expect(fallback.stats.hits).to.be(1) + expect(fallback.stats.misses).to.be(0) }) - return it('writes the cache entry to the main cache when read from the fallback cache', function () { + it('writes the cache entry to the main cache when read from the fallback cache', function () { cache = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'synced'), resourcePath: fixturesDir @@ -310,8 +299,8 @@ describe('LessCache', function () { // Read from main cache cache.readFileSync(join(fixturesDir, 'a.less')) - expect(cache.stats.hits).toBe(1) - expect(cache.stats.misses).toBe(0) + expect(cache.stats.hits).to.be(1) + expect(cache.stats.misses).to.be(0) }) }) @@ -339,7 +328,7 @@ describe('LessCache', function () { } }) - expect(cache1.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + expect(cache1.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { prop-1: 1; prop-2: 2; @@ -369,7 +358,7 @@ describe('LessCache', function () { 'imports-1/c.less': '@c: "changed";\n' }}) - expect(cache2.readFileSync(join(fixturesDir, 'imports.less'))).toBe(dedent` + expect(cache2.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { prop-1: 1; prop-2: 2; From c71e89c74bb369329a44f2401e17d446d9e1f167 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sun, 12 Mar 2017 14:32:13 +0100 Subject: [PATCH 04/11] Extract a load method from the constructor --- src/less-cache.js | 4 +++- test/less-cache.test.js | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/less-cache.js b/src/less-cache.js index 6148a7f..ceb51ff 100644 --- a/src/less-cache.js +++ b/src/less-cache.js @@ -36,9 +36,11 @@ class LessCache { if (this.fallbackDir) { this.importsFallbackDir = join(this.fallbackDir, basename(this.importsCacheDir)) } + } + load () { try { - ({importedFiles: this.importedFiles} = this.readJson(join(this.importsCacheDir, 'imports.json'))) + this.importedFiles = this.readJson(join(this.importsCacheDir, 'imports.json')).importedFiles } catch (error) {} this.setImportPaths(this.importPaths) diff --git a/test/less-cache.test.js b/test/less-cache.test.js index 684d466..47a403f 100644 --- a/test/less-cache.test.js +++ b/test/less-cache.test.js @@ -20,6 +20,7 @@ describe('LessCache', function () { importPaths: [join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')], cacheDir: join(fixturesDir, 'cache') }) + cache.load() }) describe('::cssForFile(filePath)', function () { @@ -178,6 +179,7 @@ describe('LessCache', function () { it('reuses cached CSS across cache instances', function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths()}) + cache2.load() sinon.spy(cache2, 'parseLess') cache2.readFileSync(join(fixturesDir, 'imports.less')) expect(cache2.parseLess.callCount).to.be(0) @@ -189,6 +191,7 @@ describe('LessCache', function () { it('relativizes cache paths based on the configured resource path', function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths(), resourcePath: fixturesDir}) + cache2.load() expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(false) cache2.readFileSync(join(fixturesDir, 'imports.less')) expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(true) @@ -201,6 +204,7 @@ describe('LessCache', function () { fallbackDir: cache.getDirectory(), resourcePath: fixturesDir }) + cache2.load() cache2.readFileSync(join(fixturesDir, 'imports.less')) const cache3 = new LessCache({ @@ -209,6 +213,7 @@ describe('LessCache', function () { fallbackDir: cache2.getDirectory(), resourcePath: fixturesDir }) + cache3.load() sinon.spy(cache3, 'parseLess') cache3.readFileSync(join(fixturesDir, 'imports.less')) @@ -222,6 +227,7 @@ describe('LessCache', function () { cacheDir: join(dirname(cache.getDirectory()), 'fallback'), resourcePath: fixturesDir }) + fallback.load() cache = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'synced'), @@ -229,6 +235,7 @@ describe('LessCache', function () { fallbackDir: join(dirname(cache.getDirectory()), 'fallback'), resourcePath: fixturesDir }) + cache.load() const cacheCss = cache.readFileSync(join(fixturesDir, 'a.less')) expect(cache.stats.hits).to.be(0) @@ -246,11 +253,13 @@ describe('LessCache', function () { cacheDir: join(dirname(cache.getDirectory()), 'synced'), resourcePath: fixturesDir }) + cache.load() const fallback = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'fallback'), resourcePath: fixturesDir }) + fallback.load() const cacheWithFallback = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'synced'), @@ -258,6 +267,7 @@ describe('LessCache', function () { fallbackDir: join(dirname(cache.getDirectory()), 'fallback'), resourcePath: fixturesDir }) + cacheWithFallback.load() // Prime main cache cache.readFileSync(join(fixturesDir, 'a.less')) @@ -277,11 +287,13 @@ describe('LessCache', function () { cacheDir: join(dirname(cache.getDirectory()), 'synced'), resourcePath: fixturesDir }) + cache.load() const fallback = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'fallback'), resourcePath: fixturesDir }) + fallback.load() const cacheWithFallback = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'synced'), @@ -289,6 +301,7 @@ describe('LessCache', function () { fallbackDir: join(dirname(cache.getDirectory()), 'fallback'), resourcePath: fixturesDir }) + cacheWithFallback.load() // Prime fallback cache fallback.readFileSync(join(fixturesDir, 'a.less')) @@ -327,6 +340,7 @@ describe('LessCache', function () { ` } }) + cache1.load() expect(cache1.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { @@ -357,6 +371,7 @@ describe('LessCache', function () { `, 'imports-1/c.less': '@c: "changed";\n' }}) + cache2.load() expect(cache2.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { From 7eb37314177db4e17f884dff8464802b6e9d626d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 08:55:31 +0100 Subject: [PATCH 05/11] Start making `readFileSync` and `cssForFile` asynchronous These methods will always block on the most recent `importPathsPromise`, so that we can call `load` as soon as possible when booting Atom. --- src/less-cache.js | 81 +++++++++++++++++++++----------- test/less-cache.test.js | 100 +++++++++++++++++++++++----------------- 2 files changed, 113 insertions(+), 68 deletions(-) diff --git a/src/less-cache.js b/src/less-cache.js index ceb51ff..2d57fd0 100644 --- a/src/less-cache.js +++ b/src/less-cache.js @@ -38,25 +38,17 @@ class LessCache { } } - load () { - try { - this.importedFiles = this.readJson(join(this.importsCacheDir, 'imports.json')).importedFiles - } catch (error) {} - - this.setImportPaths(this.importPaths) - + async load () { this.stats = { hits: 0, misses: 0 } + await this.setImportPaths(this.importPaths, true) } cacheDirectoryForImports (importPaths = []) { if (this.resourcePath) { - importPaths = importPaths.map(importPath => { - return this.relativize(this.resourcePath, importPath) - } - ) + importPaths = importPaths.map(importPath => this.relativize(this.resourcePath, importPath)) } return join(this.cacheDir, this.digestForContent(importPaths.join('\n'))) } @@ -83,9 +75,13 @@ class LessCache { return importedFiles } - setImportPaths (importPaths = []) { - const importedFiles = this.getImportedFiles(importPaths) + async setImportPaths () { + this.importPathsPromise = this._setImportPaths.apply(this, arguments) + return this.importPathsPromise + } + async _setImportPaths (importPaths = [], firstLoad = false) { + const importedFiles = this.getImportedFiles(importPaths) const pathsChanged = !_.isEqual(this.importPaths, importPaths) const filesChanged = !_.isEqual(this.importedFiles, importedFiles) if (pathsChanged) { @@ -93,7 +89,7 @@ class LessCache { if (this.fallbackDir) { this.importsFallbackDir = join(this.fallbackDir, basename(this.importsCacheDir)) } - } else if (filesChanged) { + } else if (filesChanged && !firstLoad) { try { fs.removeSync(this.importsCacheDir) } catch (error) { @@ -105,8 +101,7 @@ class LessCache { } } - this.writeJson(join(this.importsCacheDir, 'imports.json'), {importedFiles}) - + await this.writeJSON(join(this.importsCacheDir, 'imports.json'), {importedFiles}) this.importedFiles = importedFiles this.importPaths = importPaths } @@ -132,9 +127,17 @@ class LessCache { return importedPaths } - readJson (filePath) { return JSON.parse(fs.readFileSync(filePath)) } + readJSON (filePath) { + return readFile(filePath).then((content) => JSON.parse(content)) + } + + writeJSON (filePath, object) { + return writeFile(filePath, JSON.stringify(object)) + } + + readJsonSync (filePath) { return JSON.parse(fs.readFileSync(filePath)) } - writeJson (filePath, object) { return fs.writeFileSync(filePath, JSON.stringify(object)) } + writeJsonSync (filePath, object) { return fs.writeFileSync(filePath, JSON.stringify(object)) } digestForPath (relativeFilePath) { let lessSource = this.lessSourcesByRelativeFilePath[relativeFilePath] @@ -175,11 +178,11 @@ class LessCache { getCachedCss (filePath, digest) { let cacheEntry, fallbackDirUsed, path try { - cacheEntry = this.readJson(this.getCachePath(this.importsCacheDir, filePath)) + cacheEntry = this.readJsonSync(this.getCachePath(this.importsCacheDir, filePath)) } catch (error) { if (this.importsFallbackDir != null) { try { - cacheEntry = this.readJson(this.getCachePath(this.importsFallbackDir, filePath)) + cacheEntry = this.readJsonSync(this.getCachePath(this.importsFallbackDir, filePath)) fallbackDirUsed = true } catch (error) {} } @@ -201,9 +204,9 @@ class LessCache { if (this.syncCaches) { if (fallbackDirUsed) { - this.writeJson(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) + this.writeJsonSync(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) } else if (this.importsFallbackDir != null) { - this.writeJson(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) + this.writeJsonSync(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) } } @@ -212,10 +215,10 @@ class LessCache { putCachedCss (filePath, digest, css, imports) { const cacheEntry = {digest, css, imports, version: cacheVersion} - this.writeJson(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) + this.writeJsonSync(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) if (this.syncCaches && (this.importsFallbackDir != null)) { - return this.writeJson(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) + return this.writeJsonSync(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) } } @@ -242,7 +245,8 @@ class LessCache { // filePath: A string path to a Less file. // // Returns the compiled CSS for the given path. - readFileSync (absoluteFilePath) { + async readFileSync (absoluteFilePath) { + await this.importPathsPromise let fileContents = null if (this.resourcePath && fs.isAbsolute(absoluteFilePath)) { const relativeFilePath = this.relativize(this.resourcePath, absoluteFilePath) @@ -260,7 +264,8 @@ class LessCache { // lessContent: The contents of the filePath // // Returns the compiled CSS for the given path and lessContent - cssForFile (filePath, lessContent) { + async cssForFile (filePath, lessContent) { + await this.importPathsPromise let imports const digest = this.digestForContent(lessContent) let css = this.getCachedCss(filePath, digest) @@ -275,3 +280,27 @@ class LessCache { return css } } + +function readFile (filePath) { + return new Promise((resolve, reject) => { + fs.readFile(filePath, 'utf8', (error, contents) => { + if (error) { + reject(error) + } else { + resolve(contents) + } + }) + }) +} + +function writeFile (filePath, contents) { + return new Promise((resolve, reject) => { + fs.writeFile(filePath, contents, (error) => { + if (error) { + reject(error) + } else { + resolve() + } + }) + }) +} diff --git a/test/less-cache.test.js b/test/less-cache.test.js index 47a403f..2a3cac1 100644 --- a/test/less-cache.test.js +++ b/test/less-cache.test.js @@ -24,7 +24,7 @@ describe('LessCache', function () { }) describe('::cssForFile(filePath)', function () { - it('returns the compiled CSS for a given path and Less content', function () { + it('returns the compiled CSS for a given path and Less content', async function () { const fileLess = dedent` @import "a"; @import "b"; @@ -38,7 +38,7 @@ describe('LessCache', function () { d: @d; } ` - const css = cache.cssForFile(join(fixturesDir, 'imports.less'), fileLess) + const css = await cache.cssForFile(join(fixturesDir, 'imports.less'), fileLess) expect(css).to.be(dedent` body { a: 1; @@ -53,8 +53,8 @@ describe('LessCache', function () { describe('::readFileSync(filePath)', function () { let [css] = [] - beforeEach(function () { - css = cache.readFileSync(join(fixturesDir, 'imports.less')) + beforeEach(async function () { + css = await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(cache.stats.hits).to.be(0) expect(cache.stats.misses).to.be(1) }) @@ -70,9 +70,9 @@ describe('LessCache', function () { `) ) - it('returns the cached CSS for a given Less file path', function () { + it('returns the cached CSS for a given Less file path', async function () { sinon.spy(cache, 'parseLess') - expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` body { a: 1; b: 2; @@ -85,9 +85,9 @@ describe('LessCache', function () { expect(cache.stats.misses).to.be(1) }) - it('reflects changes to the file being read', function () { + it('reflects changes to the file being read', async function () { fs.writeFileSync(join(fixturesDir, 'imports.less'), 'body { display: block; }') - css = cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { display: block; @@ -95,9 +95,9 @@ describe('LessCache', function () { `) }) - it('reflects changes to files imported by the file being read', function () { + it('reflects changes to files imported by the file being read', async function () { fs.writeFileSync(join(fixturesDir, 'b.less'), '@b: 20;') - css = cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -108,10 +108,10 @@ describe('LessCache', function () { `) }) - it('reflects changes to files on the import path', function () { + it('reflects changes to files on the import path', async function () { fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 40;') cache.setImportPaths(cache.getImportPaths()) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -123,7 +123,7 @@ describe('LessCache', function () { fs.unlinkSync(join(fixturesDir, 'imports-1', 'c.less')) cache.setImportPaths(cache.getImportPaths()) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -135,7 +135,7 @@ describe('LessCache', function () { fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 400;') cache.setImportPaths(cache.getImportPaths()) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -146,14 +146,14 @@ describe('LessCache', function () { `) }) - it('reflect changes to the import paths array', function () { + it('reflect changes to the import paths array', async function () { sinon.spy(cache, 'parseLess') cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) - cache.readFileSync(join(fixturesDir, 'imports.less')) + await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(cache.parseLess.callCount).to.be(0) cache.setImportPaths([join(fixturesDir, 'imports-2'), join(fixturesDir, 'imports-1'), join(fixturesDir, 'import-does-not-exist')]) - css = cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFileSync(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -166,7 +166,7 @@ describe('LessCache', function () { cache.parseLess.reset() cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) - expect(cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` body { a: 1; b: 2; @@ -177,27 +177,43 @@ describe('LessCache', function () { expect(cache.parseLess.callCount).to.be(0) }) - it('reuses cached CSS across cache instances', function () { + it('reuses cached CSS across cache instances', async function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths()}) cache2.load() sinon.spy(cache2, 'parseLess') - cache2.readFileSync(join(fixturesDir, 'imports.less')) + await cache2.readFileSync(join(fixturesDir, 'imports.less')) expect(cache2.parseLess.callCount).to.be(0) }) - it('throws compile errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'invalid.less'))).to.throwError()) + it('throws compile errors', async function () { + let threwError = false + try { + await cache.readFileSync(join(fixturesDir, 'invalid.less')) + } catch (e) { + threwError = true + } + expect(threwError).to.be(true) + }) - it('throws file not found errors', () => expect(() => cache.readFileSync(join(fixturesDir, 'does-not-exist.less'))).to.throwError()) + it('throws file not found errors', async function () { + let threwError = false + try { + await cache.readFileSync(join(fixturesDir, 'does-not-exist.less')) + } catch (e) { + threwError = true + } + expect(threwError).to.be(true) + }) - it('relativizes cache paths based on the configured resource path', function () { + it('relativizes cache paths based on the configured resource path', async function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths(), resourcePath: fixturesDir}) - cache2.load() + await cache2.load() expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(false) - cache2.readFileSync(join(fixturesDir, 'imports.less')) + await cache2.readFileSync(join(fixturesDir, 'imports.less')) expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(true) }) - it('uses the fallback directory when no cache entry is found in the primary directory', function () { + it('uses the fallback directory when no cache entry is found in the primary directory', async function () { const cache2 = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'cache2'), importPaths: cache.getImportPaths(), @@ -205,7 +221,7 @@ describe('LessCache', function () { resourcePath: fixturesDir }) cache2.load() - cache2.readFileSync(join(fixturesDir, 'imports.less')) + await cache2.readFileSync(join(fixturesDir, 'imports.less')) const cache3 = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'cache3'), @@ -216,13 +232,13 @@ describe('LessCache', function () { cache3.load() sinon.spy(cache3, 'parseLess') - cache3.readFileSync(join(fixturesDir, 'imports.less')) + await cache3.readFileSync(join(fixturesDir, 'imports.less')) expect(cache3.parseLess.callCount).to.be(0) }) }) describe('when syncCaches option is set to true', function () { - it('writes the cache entry to the fallback cache when initially uncached', function () { + it('writes the cache entry to the fallback cache when initially uncached', async function () { const fallback = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'fallback'), resourcePath: fixturesDir @@ -237,18 +253,18 @@ describe('LessCache', function () { }) cache.load() - const cacheCss = cache.readFileSync(join(fixturesDir, 'a.less')) + const cacheCss = await cache.readFileSync(join(fixturesDir, 'a.less')) expect(cache.stats.hits).to.be(0) expect(cache.stats.misses).to.be(1) - const fallbackCss = fallback.readFileSync(join(fixturesDir, 'a.less')) + const fallbackCss = await fallback.readFileSync(join(fixturesDir, 'a.less')) expect(fallback.stats.hits).to.be(1) expect(fallback.stats.misses).to.be(0) expect(cacheCss).to.be(fallbackCss) }) - it('writes the cache entry to the fallback cache when read from the main cache', function () { + it('writes the cache entry to the fallback cache when read from the main cache', async function () { cache = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'synced'), resourcePath: fixturesDir @@ -270,19 +286,19 @@ describe('LessCache', function () { cacheWithFallback.load() // Prime main cache - cache.readFileSync(join(fixturesDir, 'a.less')) + await cache.readFileSync(join(fixturesDir, 'a.less')) // Read from main cache with write to fallback - cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) + await cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) // Read from fallback cache - fallback.readFileSync(join(fixturesDir, 'a.less')) + await fallback.readFileSync(join(fixturesDir, 'a.less')) expect(fallback.stats.hits).to.be(1) expect(fallback.stats.misses).to.be(0) }) - it('writes the cache entry to the main cache when read from the fallback cache', function () { + it('writes the cache entry to the main cache when read from the fallback cache', async function () { cache = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'synced'), resourcePath: fixturesDir @@ -304,13 +320,13 @@ describe('LessCache', function () { cacheWithFallback.load() // Prime fallback cache - fallback.readFileSync(join(fixturesDir, 'a.less')) + await fallback.readFileSync(join(fixturesDir, 'a.less')) // Read from fallback with write to main cache - cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) + await cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) // Read from main cache - cache.readFileSync(join(fixturesDir, 'a.less')) + await cache.readFileSync(join(fixturesDir, 'a.less')) expect(cache.stats.hits).to.be(1) expect(cache.stats.misses).to.be(0) @@ -318,7 +334,7 @@ describe('LessCache', function () { }) return describe('when providing a resource path and less sources by relative file path', () => - it("reads from the provided sources first, and falls back to reading from disk if a valid source isn't available", function () { + it("reads from the provided sources first, and falls back to reading from disk if a valid source isn't available", async function () { const cacheDir = temp.mkdirSync() const cache1 = new LessCache({ cacheDir, @@ -342,7 +358,7 @@ describe('LessCache', function () { }) cache1.load() - expect(cache1.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache1.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { prop-1: 1; prop-2: 2; @@ -373,7 +389,7 @@ describe('LessCache', function () { }}) cache2.load() - expect(cache2.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache2.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { prop-1: 1; prop-2: 2; From fe8d9cc1d644d2f1bdc35ddb0485e381d1aa3ef4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 09:20:30 +0100 Subject: [PATCH 06/11] Walk cache directories asynchronously --- src/less-cache.js | 54 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/src/less-cache.js b/src/less-cache.js index 2d57fd0..d56a150 100644 --- a/src/less-cache.js +++ b/src/less-cache.js @@ -5,7 +5,7 @@ const _ = require('underscore-plus') const fs = require('fs-plus') let less = null // Defer until it is actually used let lessFs = null // Defer until it is actually used -const walkdir = require('walkdir').sync +const walkdir = require('walkdir') const cacheVersion = 1 @@ -57,31 +57,57 @@ class LessCache { getImportPaths () { return _.clone(this.importPaths) } - getImportedFiles (importPaths) { - const importedFiles = [] - for (let importPath of importPaths) { + async getImportedFiles (importPaths) { + let importedFiles = [] + for (let i = 0; i < importPaths.length; i++) { try { - walkdir(importPath, {no_return: true}, (filePath, stat) => { - if (!stat.isFile()) { return } - if (this.resourcePath) { filePath = this.relativize(this.resourcePath, filePath) } - return importedFiles.push(filePath) - } - ) + const filePaths = await this.getFilePathsAtImportPath(importPaths[i]) + importedFiles = importedFiles.concat(filePaths) } catch (error) { continue } } - return importedFiles } - async setImportPaths () { - this.importPathsPromise = this._setImportPaths.apply(this, arguments) + getFilePathsAtImportPath (importPath) { + return new Promise((resolve, reject) => { + const filePaths = [] + const emitter = walkdir(importPath, (filePath, stat) => { + if (stat.isFile()) { + if (this.resourcePath) { + filePath = this.relativize(this.resourcePath, filePath) + } + filePaths.push(filePath) + } + }) + const disposeEmitter = () => { + emitter.removeAllListeners('error') + emitter.removeAllListeners('end') + } + emitter.on('error', (error) => { + disposeEmitter() + reject(error) + }) + emitter.on('end', () => { + disposeEmitter() + resolve(filePaths) + }) + }) + } + + async setImportPaths (importPaths, firstLoad) { + if (this.importPathsPromise) { + this.importPathsPromise = this.importPathsPromise.then(() => this._setImportPaths(importPaths, firstLoad)) + } else { + this.importPathsPromise = this._setImportPaths(importPaths, firstLoad) + } + return this.importPathsPromise } async _setImportPaths (importPaths = [], firstLoad = false) { - const importedFiles = this.getImportedFiles(importPaths) + const importedFiles = await this.getImportedFiles(importPaths) const pathsChanged = !_.isEqual(this.importPaths, importPaths) const filesChanged = !_.isEqual(this.importedFiles, importedFiles) if (pathsChanged) { From ec911dc16171b044dffa8819e316e188d39cd6d9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 09:47:23 +0100 Subject: [PATCH 07/11] Make {get,put}CachedCss asynchronous --- src/less-cache.js | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/less-cache.js b/src/less-cache.js index d56a150..54640ed 100644 --- a/src/less-cache.js +++ b/src/less-cache.js @@ -38,12 +38,12 @@ class LessCache { } } - async load () { + load () { this.stats = { hits: 0, misses: 0 } - await this.setImportPaths(this.importPaths, true) + return this.setImportPaths(this.importPaths, true) } cacheDirectoryForImports (importPaths = []) { @@ -139,7 +139,9 @@ class LessCache { lessFs.readFileSync = (filePath, ...args) => { let relativeFilePath if (this.resourcePath) { relativeFilePath = this.relativize(this.resourcePath, filePath) } - const content = this.lessSourcesByRelativeFilePath[relativeFilePath] != null ? this.lessSourcesByRelativeFilePath[relativeFilePath] : originalFsReadFileSync(filePath, ...args) + const content = this.lessSourcesByRelativeFilePath[relativeFilePath] != null ? + this.lessSourcesByRelativeFilePath[relativeFilePath] : + originalFsReadFileSync(filePath, ...args) importedPaths.push({path: relativeFilePath != null ? relativeFilePath : filePath, digest: this.digestForContent(content)}) return content } @@ -161,11 +163,7 @@ class LessCache { return writeFile(filePath, JSON.stringify(object)) } - readJsonSync (filePath) { return JSON.parse(fs.readFileSync(filePath)) } - - writeJsonSync (filePath, object) { return fs.writeFileSync(filePath, JSON.stringify(object)) } - - digestForPath (relativeFilePath) { + async digestForPath (relativeFilePath) { let lessSource = this.lessSourcesByRelativeFilePath[relativeFilePath] if (lessSource == null) { let absoluteFilePath = null @@ -174,7 +172,7 @@ class LessCache { } else { absoluteFilePath = relativeFilePath } - lessSource = fs.readFileSync(absoluteFilePath) + lessSource = await readFile(absoluteFilePath) } return this.digestForContent(lessSource) @@ -201,14 +199,14 @@ class LessCache { return join(directory, 'content', directoryPath, cacheFile) } - getCachedCss (filePath, digest) { + async getCachedCss (filePath, digest) { let cacheEntry, fallbackDirUsed, path try { - cacheEntry = this.readJsonSync(this.getCachePath(this.importsCacheDir, filePath)) + cacheEntry = await this.readJSON(this.getCachePath(this.importsCacheDir, filePath)) } catch (error) { if (this.importsFallbackDir != null) { try { - cacheEntry = this.readJsonSync(this.getCachePath(this.importsFallbackDir, filePath)) + cacheEntry = await this.readJSON(this.getCachePath(this.importsFallbackDir, filePath)) fallbackDirUsed = true } catch (error) {} } @@ -218,9 +216,10 @@ class LessCache { return } - for ({path, digest} of cacheEntry.imports) { + for (const {path, digest} of cacheEntry.imports) { try { - if (this.digestForPath(path) !== digest) { + const digestForPath = await this.digestForPath(path) + if (digestForPath !== digest) { return } } catch (error) { @@ -230,21 +229,21 @@ class LessCache { if (this.syncCaches) { if (fallbackDirUsed) { - this.writeJsonSync(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) + await this.writeJSON(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) } else if (this.importsFallbackDir != null) { - this.writeJsonSync(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) + await this.writeJSON(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) } } return cacheEntry.css } - putCachedCss (filePath, digest, css, imports) { + async putCachedCss (filePath, digest, css, imports) { const cacheEntry = {digest, css, imports, version: cacheVersion} - this.writeJsonSync(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) + await this.writeJSON(this.getCachePath(this.importsCacheDir, filePath), cacheEntry) if (this.syncCaches && (this.importsFallbackDir != null)) { - return this.writeJsonSync(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) + await this.writeJSON(this.getCachePath(this.importsFallbackDir, filePath), cacheEntry) } } @@ -271,8 +270,7 @@ class LessCache { // filePath: A string path to a Less file. // // Returns the compiled CSS for the given path. - async readFileSync (absoluteFilePath) { - await this.importPathsPromise + readFileSync (absoluteFilePath) { let fileContents = null if (this.resourcePath && fs.isAbsolute(absoluteFilePath)) { const relativeFilePath = this.relativize(this.resourcePath, absoluteFilePath) @@ -292,9 +290,10 @@ class LessCache { // Returns the compiled CSS for the given path and lessContent async cssForFile (filePath, lessContent) { await this.importPathsPromise + let imports const digest = this.digestForContent(lessContent) - let css = this.getCachedCss(filePath, digest) + let css = await this.getCachedCss(filePath, digest) if (css != null) { this.stats.hits++ return css @@ -302,7 +301,7 @@ class LessCache { this.stats.misses++ ({imports, css} = this.parseLess(filePath, lessContent)) - this.putCachedCss(filePath, digest, css, imports) + await this.putCachedCss(filePath, digest, css, imports) return css } } From f2c77423c0e66968cd10e9cafce9be59c1ec30f2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 09:49:01 +0100 Subject: [PATCH 08/11] Read less source asynchronously when not available --- src/less-cache.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/less-cache.js b/src/less-cache.js index 54640ed..f9845a3 100644 --- a/src/less-cache.js +++ b/src/less-cache.js @@ -270,14 +270,18 @@ class LessCache { // filePath: A string path to a Less file. // // Returns the compiled CSS for the given path. - readFileSync (absoluteFilePath) { + async readFileSync (absoluteFilePath) { let fileContents = null if (this.resourcePath && fs.isAbsolute(absoluteFilePath)) { const relativeFilePath = this.relativize(this.resourcePath, absoluteFilePath) fileContents = this.lessSourcesByRelativeFilePath[relativeFilePath] } - return this.cssForFile(absoluteFilePath, fileContents != null ? fileContents : fs.readFileSync(absoluteFilePath, 'utf8')) + if (fileContents == null) { + fileContents = await readFile(absoluteFilePath) + } + + return this.cssForFile(absoluteFilePath, fileContents) } // Return either cached CSS or the newly From 9296d2882590819b866462d7b778132bbcfb1a38 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 09:50:51 +0100 Subject: [PATCH 09/11] Delete importsCacheDir asynchronously --- src/less-cache.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/less-cache.js b/src/less-cache.js index f9845a3..4278ab8 100644 --- a/src/less-cache.js +++ b/src/less-cache.js @@ -117,11 +117,11 @@ class LessCache { } } else if (filesChanged && !firstLoad) { try { - fs.removeSync(this.importsCacheDir) + await deletePath(this.importsCacheDir) } catch (error) { if (error && error.code === 'ENOENT') { try { - fs.removeSync(this.importsCacheDir) // Retry once + await deletePath(this.importsCacheDir) // Retry once } catch (error) {} } } @@ -333,3 +333,15 @@ function writeFile (filePath, contents) { }) }) } + +function deletePath (path) { + return new Promise((resolve, reject) => { + fs.remove(path, (error) => { + if (error) { + reject(error) + } else { + resolve() + } + }) + }) +} From cca5bf526c76cdf05828788f93e33beaf5b49120 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 09:54:12 +0100 Subject: [PATCH 10/11] Delete unnecessary modules --- package.json | 7 ++----- test/less-cache.test.js | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5d0374f..5adcce7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "less-cache", "version": "1.0.0", "description": "Less compile cache", - "main": "./lib/less-cache", + "main": "./lib/less-cache.js", "scripts": { "test": "mocha test/**/*.test.js --compilers js:babel-register", "prepublish": "rimraf lib && babel src -d lib" @@ -38,12 +38,9 @@ "babel-register": "^6.23.0", "dedent": "^0.7.0", "expect.js": "^0.3.1", - "fs-plus": "^3.0.0", - "fstream": "^1.0.10", "mocha": "^3.2.0", "rimraf": "^2.6.1", "sinon": "^1.17.7", - "temp": "^0.8.3", - "tmp": "0.0.28" + "temp": "^0.8.3" } } diff --git a/test/less-cache.test.js b/test/less-cache.test.js index 2a3cac1..5139e89 100644 --- a/test/less-cache.test.js +++ b/test/less-cache.test.js @@ -5,7 +5,6 @@ const fs = require('fs-plus') const {dirname, join} = require('path') const temp = require('temp').track() -const fstream = require('fstream') const dedent = require('dedent') const LessCache = require('../src/less-cache') From ec91e8f4368859557ebb91f2d7e09f1094899f8d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 10:03:07 +0100 Subject: [PATCH 11/11] Document the new APIs --- README.md | 16 +++++++++--- src/less-cache.js | 8 +++--- test/less-cache.test.js | 54 ++++++++++++++++++++--------------------- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 7851c7e..48335a1 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,17 @@ Caches the compiled `.less` files as `.css`. npm install less-cache ``` -```coffeescript -LessCache = require 'less-cache' +```javascript +const LessCache = require('less-cache') +const cache = new LessCache({cacheDir: '/tmp/less-cache'}) -cache = new LessCache(cacheDir: '/tmp/less-cache') -css = cache.readFileSync('/Users/me/apps/static/styles.less') +// This method returns a {Promise}, but you can avoid waiting on it as it will +// be waited upon automatically when calling `readFile` or `cssForFile` later. +cache.load() +const css1 = await cache.readFile('/Users/me/apps/static/styles.less') + +// Similarly to `load`, this method will return a {Promise} and subsequent calls +// to `readFile` or `cssForFile` will automatically wait on it. +cache.setImportPaths(['path-1', 'path-2']) +const css2 = await cache.readFile('/Users/me/apps/static/styles.less') ``` diff --git a/src/less-cache.js b/src/less-cache.js index 4278ab8..1d82287 100644 --- a/src/less-cache.js +++ b/src/less-cache.js @@ -36,13 +36,13 @@ class LessCache { if (this.fallbackDir) { this.importsFallbackDir = join(this.fallbackDir, basename(this.importsCacheDir)) } - } - - load () { this.stats = { hits: 0, misses: 0 } + } + + load () { return this.setImportPaths(this.importPaths, true) } @@ -270,7 +270,7 @@ class LessCache { // filePath: A string path to a Less file. // // Returns the compiled CSS for the given path. - async readFileSync (absoluteFilePath) { + async readFile (absoluteFilePath) { let fileContents = null if (this.resourcePath && fs.isAbsolute(absoluteFilePath)) { const relativeFilePath = this.relativize(this.resourcePath, absoluteFilePath) diff --git a/test/less-cache.test.js b/test/less-cache.test.js index 5139e89..9d78b80 100644 --- a/test/less-cache.test.js +++ b/test/less-cache.test.js @@ -49,11 +49,11 @@ describe('LessCache', function () { }) }) - describe('::readFileSync(filePath)', function () { + describe('::readFile(filePath)', function () { let [css] = [] beforeEach(async function () { - css = await cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFile(join(fixturesDir, 'imports.less')) expect(cache.stats.hits).to.be(0) expect(cache.stats.misses).to.be(1) }) @@ -71,7 +71,7 @@ describe('LessCache', function () { it('returns the cached CSS for a given Less file path', async function () { sinon.spy(cache, 'parseLess') - expect(await cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache.readFile(join(fixturesDir, 'imports.less'))).to.be(dedent` body { a: 1; b: 2; @@ -86,7 +86,7 @@ describe('LessCache', function () { it('reflects changes to the file being read', async function () { fs.writeFileSync(join(fixturesDir, 'imports.less'), 'body { display: block; }') - css = await cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFile(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { display: block; @@ -96,7 +96,7 @@ describe('LessCache', function () { it('reflects changes to files imported by the file being read', async function () { fs.writeFileSync(join(fixturesDir, 'b.less'), '@b: 20;') - css = await cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFile(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -110,7 +110,7 @@ describe('LessCache', function () { it('reflects changes to files on the import path', async function () { fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 40;') cache.setImportPaths(cache.getImportPaths()) - css = await cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFile(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -122,7 +122,7 @@ describe('LessCache', function () { fs.unlinkSync(join(fixturesDir, 'imports-1', 'c.less')) cache.setImportPaths(cache.getImportPaths()) - css = await cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFile(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -134,7 +134,7 @@ describe('LessCache', function () { fs.writeFileSync(join(fixturesDir, 'imports-1', 'd.less'), '@d: 400;') cache.setImportPaths(cache.getImportPaths()) - css = await cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFile(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -148,11 +148,11 @@ describe('LessCache', function () { it('reflect changes to the import paths array', async function () { sinon.spy(cache, 'parseLess') cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) - await cache.readFileSync(join(fixturesDir, 'imports.less')) + await cache.readFile(join(fixturesDir, 'imports.less')) expect(cache.parseLess.callCount).to.be(0) cache.setImportPaths([join(fixturesDir, 'imports-2'), join(fixturesDir, 'imports-1'), join(fixturesDir, 'import-does-not-exist')]) - css = await cache.readFileSync(join(fixturesDir, 'imports.less')) + css = await cache.readFile(join(fixturesDir, 'imports.less')) expect(css).to.be(dedent` body { a: 1; @@ -165,7 +165,7 @@ describe('LessCache', function () { cache.parseLess.reset() cache.setImportPaths([join(fixturesDir, 'imports-1'), join(fixturesDir, 'imports-2')]) - expect(await cache.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache.readFile(join(fixturesDir, 'imports.less'))).to.be(dedent` body { a: 1; b: 2; @@ -180,14 +180,14 @@ describe('LessCache', function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths()}) cache2.load() sinon.spy(cache2, 'parseLess') - await cache2.readFileSync(join(fixturesDir, 'imports.less')) + await cache2.readFile(join(fixturesDir, 'imports.less')) expect(cache2.parseLess.callCount).to.be(0) }) it('throws compile errors', async function () { let threwError = false try { - await cache.readFileSync(join(fixturesDir, 'invalid.less')) + await cache.readFile(join(fixturesDir, 'invalid.less')) } catch (e) { threwError = true } @@ -197,7 +197,7 @@ describe('LessCache', function () { it('throws file not found errors', async function () { let threwError = false try { - await cache.readFileSync(join(fixturesDir, 'does-not-exist.less')) + await cache.readFile(join(fixturesDir, 'does-not-exist.less')) } catch (e) { threwError = true } @@ -208,7 +208,7 @@ describe('LessCache', function () { const cache2 = new LessCache({cacheDir: cache.getDirectory(), importPaths: cache.getImportPaths(), resourcePath: fixturesDir}) await cache2.load() expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(false) - await cache2.readFileSync(join(fixturesDir, 'imports.less')) + await cache2.readFile(join(fixturesDir, 'imports.less')) expect(fs.existsSync(join(cache2.importsCacheDir, 'content', 'imports.json'))).to.be(true) }) @@ -220,7 +220,7 @@ describe('LessCache', function () { resourcePath: fixturesDir }) cache2.load() - await cache2.readFileSync(join(fixturesDir, 'imports.less')) + await cache2.readFile(join(fixturesDir, 'imports.less')) const cache3 = new LessCache({ cacheDir: join(dirname(cache.getDirectory()), 'cache3'), @@ -231,7 +231,7 @@ describe('LessCache', function () { cache3.load() sinon.spy(cache3, 'parseLess') - await cache3.readFileSync(join(fixturesDir, 'imports.less')) + await cache3.readFile(join(fixturesDir, 'imports.less')) expect(cache3.parseLess.callCount).to.be(0) }) }) @@ -252,11 +252,11 @@ describe('LessCache', function () { }) cache.load() - const cacheCss = await cache.readFileSync(join(fixturesDir, 'a.less')) + const cacheCss = await cache.readFile(join(fixturesDir, 'a.less')) expect(cache.stats.hits).to.be(0) expect(cache.stats.misses).to.be(1) - const fallbackCss = await fallback.readFileSync(join(fixturesDir, 'a.less')) + const fallbackCss = await fallback.readFile(join(fixturesDir, 'a.less')) expect(fallback.stats.hits).to.be(1) expect(fallback.stats.misses).to.be(0) @@ -285,13 +285,13 @@ describe('LessCache', function () { cacheWithFallback.load() // Prime main cache - await cache.readFileSync(join(fixturesDir, 'a.less')) + await cache.readFile(join(fixturesDir, 'a.less')) // Read from main cache with write to fallback - await cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) + await cacheWithFallback.readFile(join(fixturesDir, 'a.less')) // Read from fallback cache - await fallback.readFileSync(join(fixturesDir, 'a.less')) + await fallback.readFile(join(fixturesDir, 'a.less')) expect(fallback.stats.hits).to.be(1) expect(fallback.stats.misses).to.be(0) @@ -319,13 +319,13 @@ describe('LessCache', function () { cacheWithFallback.load() // Prime fallback cache - await fallback.readFileSync(join(fixturesDir, 'a.less')) + await fallback.readFile(join(fixturesDir, 'a.less')) // Read from fallback with write to main cache - await cacheWithFallback.readFileSync(join(fixturesDir, 'a.less')) + await cacheWithFallback.readFile(join(fixturesDir, 'a.less')) // Read from main cache - await cache.readFileSync(join(fixturesDir, 'a.less')) + await cache.readFile(join(fixturesDir, 'a.less')) expect(cache.stats.hits).to.be(1) expect(cache.stats.misses).to.be(0) @@ -357,7 +357,7 @@ describe('LessCache', function () { }) cache1.load() - expect(await cache1.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache1.readFile(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { prop-1: 1; prop-2: 2; @@ -388,7 +388,7 @@ describe('LessCache', function () { }}) cache2.load() - expect(await cache2.readFileSync(join(fixturesDir, 'imports.less'))).to.be(dedent` + expect(await cache2.readFile(join(fixturesDir, 'imports.less'))).to.be(dedent` some-selector { prop-1: 1; prop-2: 2;