forked from bartholomej/ngx-translate-extract
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add an optional cache that speeds up consecutive runs (#38)
used like: ngx-translate-extract --cache-file node_modules/.i18n-cache/my-cache-file --input ./src --output ./src/i18n/{da,en}.json
- Loading branch information
Showing
6 changed files
with
152 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface CacheInterface<RESULT extends object = object> { | ||
persist(): void; | ||
get<KEY extends string>(uniqueContents: KEY, generator: () => RESULT): RESULT; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import type { CacheInterface } from './cache-interface.js'; | ||
import crypto from 'node:crypto'; | ||
import fs from 'node:fs'; | ||
import path from 'node:path'; | ||
import { fileURLToPath } from 'node:url'; | ||
|
||
const getHash = (value: string) => crypto.createHash('sha256').update(value).digest('hex'); | ||
|
||
export class FileCache<RESULT extends object = object> implements CacheInterface<RESULT> { | ||
private tapped: Record<string, RESULT> = {}; | ||
private cached?: Readonly<Record<string, RESULT>> = undefined; | ||
private originalCache?: string; | ||
private versionHash?: string; | ||
|
||
constructor(private cacheFile: string) {} | ||
|
||
public get<KEY extends string>(uniqueContents: KEY, generator: () => RESULT): RESULT { | ||
if (!this.cached) { | ||
this.readCache(); | ||
this.versionHash = this.getVersionHash(); | ||
} | ||
|
||
const key = getHash(`${this.versionHash}${uniqueContents}`); | ||
|
||
if (key in this.cached) { | ||
this.tapped[key] = this.cached[key]; | ||
|
||
return this.cached[key]; | ||
} | ||
|
||
return (this.tapped[key] = generator()); | ||
} | ||
|
||
public persist(): void { | ||
const newCache = JSON.stringify(this.sortByKey(this.tapped), null, 2); | ||
if (newCache === this.originalCache) { | ||
return; | ||
} | ||
|
||
const file = this.getCacheFile(); | ||
const dir = path.dirname(file); | ||
|
||
const stats = fs.statSync(dir, { throwIfNoEntry: false }); | ||
if (!stats) { | ||
fs.mkdirSync(dir); | ||
} | ||
|
||
const tmpFile = `${file}~${getHash(newCache)}`; | ||
|
||
fs.writeFileSync(tmpFile, newCache, { encoding: 'utf-8' }); | ||
fs.rmSync(file, { force: true, recursive: false }); | ||
fs.renameSync(tmpFile, file); | ||
} | ||
|
||
private sortByKey(unordered: Record<string, RESULT>): Record<string, RESULT> { | ||
return Object.keys(unordered) | ||
.sort() | ||
.reduce((obj, key) => { | ||
obj[key] = unordered[key]; | ||
return obj; | ||
}, {} as Record<string, RESULT>); | ||
} | ||
|
||
private readCache(): void { | ||
try { | ||
this.originalCache = fs.readFileSync(this.getCacheFile(), { encoding: 'utf-8' }); | ||
this.cached = JSON.parse(this.originalCache) ?? {}; | ||
} catch { | ||
this.originalCache = undefined; | ||
this.cached = {}; | ||
} | ||
} | ||
|
||
private getVersionHash(): string { | ||
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); | ||
const packageJson = fs.readFileSync(path.join(projectRoot, 'package.json'), { encoding: 'utf-8' }); | ||
|
||
return getHash(packageJson); | ||
} | ||
|
||
private getCacheFile(): string { | ||
return `${this.cacheFile}-ngx-translate-extract-cache.json`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { CacheInterface } from './cache-interface.js'; | ||
|
||
export class NullCache<RESULT extends object = object> implements CacheInterface<RESULT> { | ||
persist() {} | ||
get<KEY extends string>(_uniqueContents: KEY, generator: () => RESULT): RESULT { | ||
return generator(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters