Skip to content

Commit

Permalink
Merge pull request #147 from mattlewis92/incremental-build-cache
Browse files Browse the repository at this point in the history
perf: cache results of fs.stat call for each build to improve incremental performance
  • Loading branch information
glromeo committed Sep 6, 2023
2 parents 12095f3 + dee945d commit 965ceb6
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 8 deletions.
18 changes: 12 additions & 6 deletions src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import {promises as fsp, Stats} from 'fs'
type OnLoadCallback = (args: OnLoadArgs) => (OnLoadResult | Promise<OnLoadResult>)
type PluginLoadCallback = (path: string) => (OnLoadResult | Promise<OnLoadResult>)

function collectStats(watchFiles): Promise<Stats[]> {
return Promise.all(watchFiles.map(filename => fsp.stat(filename)))
function collectStats(watchFiles: string[], fsStatCache: Map<string, Stats>): Promise<Stats[]> {
return Promise.all(watchFiles.map(async filename => {
if (!fsStatCache.has(filename)) {
const stats = await fsp.stat(filename)
fsStatCache.set(filename, stats)
}
return fsStatCache.get(filename) as Stats
}))
}

function maxMtimeMs(stats: Stats[]) {
Expand All @@ -23,15 +29,15 @@ function getCache(options: SassPluginOptions): Map<string, CachedResult> | undef
}
}

export function useCache(options: SassPluginOptions = {}, loadCallback: PluginLoadCallback): OnLoadCallback {
export function useCache(options: SassPluginOptions = {}, fsStatCache: Map<string, Stats>, loadCallback: PluginLoadCallback): OnLoadCallback {
const cache = getCache(options)
if (cache) {
return async ({path}: OnLoadArgs) => {
try {
let cached = cache.get(path)
if (cached) {
let watchFiles = cached.result.watchFiles!
let stats = await collectStats(watchFiles)
let stats = await collectStats(watchFiles, fsStatCache)
for (const {mtimeMs} of stats) {
if (mtimeMs > cached.mtimeMs) {
cached.result = await loadCallback(watchFiles[0])
Expand All @@ -42,7 +48,7 @@ export function useCache(options: SassPluginOptions = {}, loadCallback: PluginLo
} else {
let result = await loadCallback(path)
cached = {
mtimeMs: maxMtimeMs(await collectStats(result.watchFiles)),
mtimeMs: maxMtimeMs(await collectStats(result.watchFiles!, fsStatCache)),
result
}
cache.set(path, cached)
Expand All @@ -59,4 +65,4 @@ export function useCache(options: SassPluginOptions = {}, loadCallback: PluginLo
} else {
return ({path}) => loadCallback(path)
}
}
}
7 changes: 5 additions & 2 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function sassPlugin(options: SassPluginOptions = {}): Plugin {

return {
name: 'sass-plugin',
setup({initialOptions, onResolve, onLoad, resolve}) {
setup({initialOptions, onResolve, onLoad, resolve, onStart}) {

options.loadPaths = Array.from(new Set([
...options.loadPaths || modulesPaths(initialOptions.absWorkingDir),
Expand All @@ -49,6 +49,9 @@ export function sassPlugin(options: SassPluginOptions = {}): Plugin {
})
}

const fsStatCache = new Map()
onStart(() => fsStatCache.clear())

const transform = options.transform ? options.transform.bind(options) : null

const cssChunks: Record<string, string | Uint8Array | undefined> = {}
Expand All @@ -71,7 +74,7 @@ export function sassPlugin(options: SassPluginOptions = {}): Plugin {

const renderSync = createRenderer(options, options.sourceMap ?? sourcemap)

onLoad({filter: options.filter ?? DEFAULT_FILTER}, useCache(options, async path => {
onLoad({filter: options.filter ?? DEFAULT_FILTER}, useCache(options, fsStatCache, async path => {
try {
let {cssText, watchFiles, warnings} = renderSync(path)
if (!warnings) {
Expand Down

0 comments on commit 965ceb6

Please sign in to comment.