|
@@ -5,7 +5,7 @@ import { createReadStream } from "fs"
|
|
|
import stableStringify from "safe-stable-stringify"
|
|
import stableStringify from "safe-stable-stringify"
|
|
|
import {
|
|
import {
|
|
|
slugifyString,
|
|
slugifyString,
|
|
|
- checkPathExists,
|
|
|
|
|
|
|
+ checkFilesExist,
|
|
|
getValueAtPath,
|
|
getValueAtPath,
|
|
|
removeCwd,
|
|
removeCwd,
|
|
|
getDeepestPropertiesForKey,
|
|
getDeepestPropertiesForKey,
|
|
@@ -69,37 +69,47 @@ function getStatePropsHash(state, props) {
|
|
|
export async function checkCache(cacheKey, currentState, opts) {
|
|
export async function checkCache(cacheKey, currentState, opts) {
|
|
|
const name = slugifyString(cacheKey)
|
|
const name = slugifyString(cacheKey)
|
|
|
const existingCacheObject = await readCache(opts.cacheDir, name)
|
|
const existingCacheObject = await readCache(opts.cacheDir, name)
|
|
|
- if (existingCacheObject) {
|
|
|
|
|
- if (opts.ignoreExisting || (await checkPathExists(existingCacheObject.taskResult.paths, opts.outDir))) {
|
|
|
|
|
|
|
+ if (existingCacheObject.exists) {
|
|
|
|
|
+ const fileChecks = opts.ignoreExisting ? {} : await checkFilesExist(existingCacheObject.content.taskResult.paths, opts.outDir)
|
|
|
|
|
+ if (!fileChecks.absent?.length) {
|
|
|
const stateHash = getStatePropsHash(
|
|
const stateHash = getStatePropsHash(
|
|
|
currentState,
|
|
currentState,
|
|
|
- existingCacheObject.deps.state.props,
|
|
|
|
|
|
|
+ existingCacheObject.content.deps.state.props,
|
|
|
)
|
|
)
|
|
|
- if (stateHash === existingCacheObject.deps.state.hash) {
|
|
|
|
|
|
|
+ if (stateHash === existingCacheObject.content.deps.state.hash) {
|
|
|
try {
|
|
try {
|
|
|
- await getFileHashes(existingCacheObject.deps.paths)
|
|
|
|
|
- return { hit: true, taskResult: existingCacheObject.taskResult }
|
|
|
|
|
|
|
+ await getFileHashes(existingCacheObject.content.deps.paths)
|
|
|
|
|
+ return { hit: true, taskResult: existingCacheObject.content.taskResult, filePath: existingCacheObject.filePath }
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
const updates = {
|
|
const updates = {
|
|
|
deps: {
|
|
deps: {
|
|
|
paths: [e],
|
|
paths: [e],
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- return { hit: false, reason: "File hash mismatch", updates }
|
|
|
|
|
|
|
+ return { hit: false, reason: `File hash mismatch: ${e.filePath}`, updates, filePath: existingCacheObject.filePath }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
const updates = {
|
|
const updates = {
|
|
|
deps: {
|
|
deps: {
|
|
|
state: {
|
|
state: {
|
|
|
- ...existingCacheObject.deps.state,
|
|
|
|
|
|
|
+ ...existingCacheObject.content.deps.state,
|
|
|
hash: stateHash,
|
|
hash: stateHash,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- return { hit: false, reason: "State hash mismatch", updates }
|
|
|
|
|
|
|
+ const stateValuesList = existingCacheObject.content.deps.state.values
|
|
|
|
|
+ if (stateValuesList) {
|
|
|
|
|
+ const mismatchedStateProps = existingCacheObject.content.deps.state.props.filter((stateProp, index) => {
|
|
|
|
|
+ const currentValue = getValueAtPath(currentState, stateProp)
|
|
|
|
|
+ const cachedValue = stateValuesList[index]
|
|
|
|
|
+ return currentValue !== cachedValue
|
|
|
|
|
+ })
|
|
|
|
|
+ return { hit: false, reason: `State hash mismatch: ${mismatchedStateProps.join(", ")}`, updates, filePath: existingCacheObject.filePath }
|
|
|
|
|
+ }
|
|
|
|
|
+ return { hit: false, reason: "State hash mismatch (no values were found in cache)", updates, filePath: existingCacheObject.filePath }
|
|
|
}
|
|
}
|
|
|
if (opts.clean) {
|
|
if (opts.clean) {
|
|
|
- const outFiles = existingCacheObject.taskResult.paths
|
|
|
|
|
|
|
+ const outFiles = existingCacheObject.content.taskResult.paths
|
|
|
await Promise.all(
|
|
await Promise.all(
|
|
|
outFiles.map(
|
|
outFiles.map(
|
|
|
async outFile =>
|
|
async outFile =>
|
|
@@ -107,9 +117,9 @@ export async function checkCache(cacheKey, currentState, opts) {
|
|
|
),
|
|
),
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
- return { hit: false, reason: "Missing output file(s)" }
|
|
|
|
|
|
|
+ return { hit: false, reason: `Missing output file(s): ${fileChecks.absent.join(", ")}`, filePath: existingCacheObject.filePath }
|
|
|
}
|
|
}
|
|
|
- return { hit: false, reason: "Missing cache file" }
|
|
|
|
|
|
|
+ return { hit: false, reason: `Missing cache file: ${existingCacheObject.filePath}`, filePath: existingCacheObject.filePath }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export async function updateCache(
|
|
export async function updateCache(
|
|
@@ -119,6 +129,7 @@ export async function updateCache(
|
|
|
stateDeps,
|
|
stateDeps,
|
|
|
taskResult,
|
|
taskResult,
|
|
|
updates,
|
|
updates,
|
|
|
|
|
+ includeStateValues,
|
|
|
) {
|
|
) {
|
|
|
await fs.mkdir(cacheDir, { recursive: true })
|
|
await fs.mkdir(cacheDir, { recursive: true })
|
|
|
const name = slugifyString(cacheKey)
|
|
const name = slugifyString(cacheKey)
|
|
@@ -131,6 +142,7 @@ export async function updateCache(
|
|
|
),
|
|
),
|
|
|
}
|
|
}
|
|
|
const statePropsList = Object.keys(deps.state)
|
|
const statePropsList = Object.keys(deps.state)
|
|
|
|
|
+ const stateValuesList = Object.values(deps.state)
|
|
|
const updatesStateHash = updates?.deps?.state?.props || []
|
|
const updatesStateHash = updates?.deps?.state?.props || []
|
|
|
const stateDepsHash =
|
|
const stateDepsHash =
|
|
|
JSON.stringify(statePropsList) === JSON.stringify(updatesStateHash)
|
|
JSON.stringify(statePropsList) === JSON.stringify(updatesStateHash)
|
|
@@ -166,6 +178,9 @@ export async function updateCache(
|
|
|
},
|
|
},
|
|
|
taskResult,
|
|
taskResult,
|
|
|
}
|
|
}
|
|
|
|
|
+ if (includeStateValues) {
|
|
|
|
|
+ cacheObject.deps.state.values = stateValuesList
|
|
|
|
|
+ }
|
|
|
return await writeCache(cacheDir, name, cacheObject)
|
|
return await writeCache(cacheDir, name, cacheObject)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -181,16 +196,17 @@ async function writeCache(cacheDir, name, cache) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function readCache(cacheDir, name) {
|
|
async function readCache(cacheDir, name) {
|
|
|
|
|
+ const filePath = path.join(cacheDir, `${name}.json`)
|
|
|
if (!cacheDir) {
|
|
if (!cacheDir) {
|
|
|
- return false
|
|
|
|
|
|
|
+ return { exists: false, filePath }
|
|
|
}
|
|
}
|
|
|
try {
|
|
try {
|
|
|
const content = await fs.readFile(
|
|
const content = await fs.readFile(
|
|
|
- path.join(cacheDir, `${name}.json`),
|
|
|
|
|
|
|
+ filePath,
|
|
|
"utf8",
|
|
"utf8",
|
|
|
)
|
|
)
|
|
|
- return JSON.parse(content)
|
|
|
|
|
- } catch {
|
|
|
|
|
- return false
|
|
|
|
|
|
|
+ return { exists: true, content: JSON.parse(content), filePath }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ return { exists: false, filePath, reason: e }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|