diff --git a/editor/src/components/assets.ts b/editor/src/components/assets.ts index 39e02fe1bf8f..1d57845374fb 100644 --- a/editor/src/components/assets.ts +++ b/editor/src/components/assets.ts @@ -19,7 +19,6 @@ import { dropLeadingSlash } from './filebrowser/filepath-utils' import { assertNever, fastForEach } from '../core/shared/utils' import { mapValues, propOrNull } from '../core/shared/object-utils' import { emptySet } from '../core/shared/set-utils' -import { sha1 } from 'sha.js' import type { GithubFileChanges, TreeConflicts } from '../core/shared/github/helpers' import type { FileChecksumsWithFile } from './editor/store/editor-state' import { memoize, valueDependentCache } from '../core/shared/memoize' @@ -34,6 +33,8 @@ import type { } from 'utopia-shared/src/types/assets' import { filtered, fromField, fromTypeGuard } from '../core/shared/optics/optic-creators' import { anyBy, toArrayOf } from '../core/shared/optics/optic-utilities' +import { gitBlobChecksumFromBuffer } from '../core/shared/file-utils' + export type { AssetFileWithFileName, ProjectContentTreeRoot, @@ -60,18 +61,6 @@ export function getAllProjectAssetFiles( return allProjectAssets } -function getSHA1ChecksumInner(contents: string | Buffer): string { - return new sha1().update(contents).digest('hex') -} - -// Memoized because it can be called for the same piece of code more than once before the -// checksum gets cached. For example in the canvas strategies and the regular dispatch flow, which don't share -// those cached checksum objects. -export const getSHA1Checksum = memoize(getSHA1ChecksumInner, { - maxSize: 10, - matchesArg: (first, second) => first === second, -}) - export function gitBlobChecksumFromBase64(base64: string): string { return gitBlobChecksumFromBuffer(Buffer.from(base64, 'base64')) } @@ -80,16 +69,6 @@ export function gitBlobChecksumFromText(text: string): string { return gitBlobChecksumFromBuffer(Buffer.from(text, 'utf8')) } -export function gitBlobChecksumFromBuffer(buffer: Buffer): string { - // This function returns the same SHA1 checksum string that git would return for the same contents. - // Given the contents in the buffer variable, the final checksum is calculated by hashing - // a string built as "". The prefix looks like "blob ". - // Ref: https://git-scm.com/book/en/v2/Git-Internals-Git-Objects - const prefix = Buffer.from(`blob ${buffer.byteLength}\0`) - const wrapped = Buffer.concat([prefix, buffer]) - return getSHA1Checksum(wrapped) -} - export function checkFilesHaveSameContent(first: ProjectFile, second: ProjectFile): boolean { switch (first.type) { case 'DIRECTORY': diff --git a/editor/src/components/editor/server.ts b/editor/src/components/editor/server.ts index a689cbedd653..6b6e19a955c8 100644 --- a/editor/src/components/editor/server.ts +++ b/editor/src/components/editor/server.ts @@ -20,7 +20,7 @@ import type { LoginState } from '../../uuiui-deps' import urljoin from 'url-join' import JSZip from 'jszip' import type { AssetFileWithFileName } from '../assets' -import { gitBlobChecksumFromBuffer } from '../assets' +import { gitBlobChecksumFromBuffer } from '../../core/shared/file-utils' import { isLoginLost } from '../../common/user' import { notice } from '../common/notice' import type { EditorDispatch } from './action-types' diff --git a/editor/src/core/model/project-import.ts b/editor/src/core/model/project-import.ts index 089a5d51f1c5..8b0c3b243de4 100644 --- a/editor/src/core/model/project-import.ts +++ b/editor/src/core/model/project-import.ts @@ -13,7 +13,7 @@ import { import { fileTypeFromFileName } from './project-file-utils' import { assetResultForBase64, getFileExtension, imageResultForBase64 } from '../shared/file-utils' import type { ProjectContentTreeRoot } from '../../components/assets' -import { gitBlobChecksumFromBuffer } from '../../components/assets' +import { gitBlobChecksumFromBuffer } from '../shared/file-utils' import { addFileToProjectContents, walkContentsTreeAsync } from '../../components/assets' import type { Either } from '../shared/either' import { isRight, left, right } from '../shared/either' diff --git a/editor/src/core/shared/file-utils.ts b/editor/src/core/shared/file-utils.ts index be0d00383c96..d16a5720e90f 100644 --- a/editor/src/core/shared/file-utils.ts +++ b/editor/src/core/shared/file-utils.ts @@ -1,4 +1,5 @@ -import { gitBlobChecksumFromBuffer } from '../../components/assets' +import { memoize } from './memoize' +import { sha1 } from 'sha.js' import stringHash from 'string-hash' import type { Size } from './math-utils' import { size } from './math-utils' @@ -263,3 +264,25 @@ export function isJsOrTsFile(filename: string): boolean { export function isParseableFile(filename: string): boolean { return isJsOrTsFile(filename) } + +export function gitBlobChecksumFromBuffer(buffer: Buffer): string { + // This function returns the same SHA1 checksum string that git would return for the same contents. + // Given the contents in the buffer variable, the final checksum is calculated by hashing + // a string built as "". The prefix looks like "blob ". + // Ref: https://git-scm.com/book/en/v2/Git-Internals-Git-Objects + const prefix = Buffer.from(`blob ${buffer.byteLength}\0`) + const wrapped = Buffer.concat([prefix, buffer]) + return getSHA1Checksum(wrapped) +} + +function getSHA1ChecksumInner(contents: string | Buffer): string { + return new sha1().update(contents).digest('hex') +} + +// Memoized because it can be called for the same piece of code more than once before the +// checksum gets cached. For example in the canvas strategies and the regular dispatch flow, which don't share +// those cached checksum objects. +export const getSHA1Checksum = memoize(getSHA1ChecksumInner, { + maxSize: 10, + matchesArg: (first, second) => first === second, +}) diff --git a/editor/src/core/workers/parser-printer/parse-cache-utils.worker.ts b/editor/src/core/workers/parser-printer/parse-cache-utils.worker.ts index 851fef617172..463be361a299 100644 --- a/editor/src/core/workers/parser-printer/parse-cache-utils.worker.ts +++ b/editor/src/core/workers/parser-printer/parse-cache-utils.worker.ts @@ -1,3 +1,4 @@ +import { isParseableFile } from '../../../core/shared/file-utils' import { URL_HASH } from '../../../common/env-vars' import { type ParseCacheOptions } from '../../../core/shared/parse-cache-utils' import { @@ -33,15 +34,22 @@ function logCacheMessage(parsingCacheOptions: ParseCacheOptions, ...messages: (s } } +function isArbitraryCodeFile(filename: string): boolean { + return filename === ARBITRARY_CODE_FILE_NAME +} + function stringIdentifiers(filename: string, content: string): (string | object)[] { - if (filename === ARBITRARY_CODE_FILE_NAME) { + if (isArbitraryCodeFile(filename)) { return ['code', [{ content: content }]] } return [filename] } -function shouldSkipCacheForFile(filename: string, parsingCacheOptions: ParseCacheOptions): boolean { - return filename === ARBITRARY_CODE_FILE_NAME && !parsingCacheOptions.cacheArbitraryCode +function shouldUseCacheForFile(filename: string, parsingCacheOptions: ParseCacheOptions): boolean { + return ( + isParseableFile(filename) && + (!isArbitraryCodeFile(filename) || parsingCacheOptions.cacheArbitraryCode) + ) } export function getParseCacheVersion(): string { @@ -59,7 +67,7 @@ export async function getParseResultFromCache( parsingCacheOptions: ParseCacheOptions, ): Promise { const { filename, content } = file - if (shouldSkipCacheForFile(filename, parsingCacheOptions)) { + if (!shouldUseCacheForFile(filename, parsingCacheOptions)) { return null } const cacheKey = getCacheKey(filename) @@ -80,12 +88,12 @@ export async function storeParseResultInCache( parsingCacheOptions: ParseCacheOptions, ): Promise { const { filename, content } = file - if (shouldSkipCacheForFile(filename, parsingCacheOptions)) { + if (!shouldUseCacheForFile(filename, parsingCacheOptions)) { return } logCacheMessage(parsingCacheOptions, 'Caching', ...stringIdentifiers(filename, content)) const cacheKey = getCacheKey(filename) - if (filename === ARBITRARY_CODE_FILE_NAME) { + if (isArbitraryCodeFile(filename)) { // for the special filename 'code.tsx', we store multiple contents, so we need to read it first const cachedResult = (await getParseCacheStore().getItem(cacheKey)) ?? {} // limit the arbitrary code cache keys size