@@ -11,6 +11,7 @@ import {
1111} from '@babel/core'
1212import readBabelrcUp from 'read-babelrc-up'
1313import babelPluginReactIntl from 'babel-plugin-react-intl'
14+ import fileEntryCache , { FileDescriptor } from 'file-entry-cache'
1415
1516type LocaleMap = Record < string , Record < string , { } > >
1617
@@ -67,6 +68,8 @@ type Options = {
6768 [ key : string ] : unknown
6869 defaultLocale ?: string
6970 cwd ?: string
71+ cache ?: boolean
72+ cacheLocation ?: string
7073 withDescriptions ?: boolean
7174}
7275
@@ -76,6 +79,17 @@ type Message = {
7679 description : string
7780}
7881
82+ type CacheData = {
83+ defaultLocale : string
84+ localeMap : LocaleMap
85+ }
86+
87+ type File = FileDescriptor & {
88+ meta : {
89+ data : CacheData
90+ }
91+ }
92+
7993// eslint-disable-next-line max-lines-per-function
8094export default async (
8195 locales : string [ ] ,
@@ -84,6 +98,8 @@ export default async (
8498 defaultLocale = 'en' ,
8599 withDescriptions = false ,
86100 cwd = process . cwd ( ) ,
101+ cache : cacheEnabled = false ,
102+ cacheLocation = '.extract-react-intl-messages-cache' ,
87103 ...pluginOptions
88104 } : Options = { }
89105) => {
@@ -122,6 +138,19 @@ export default async (
122138 } )
123139 }
124140
141+ const files : string [ ] = await pify ( glob ) ( pattern )
142+ if ( files . length === 0 ) {
143+ throw new Error ( `File not found (${ pattern } )` )
144+ }
145+
146+ const cachePath = path . resolve ( cacheLocation )
147+ const cacheDirname = path . dirname ( cachePath )
148+ const cacheBasename = path . basename ( cachePath )
149+
150+ const cache = cacheEnabled
151+ ? fileEntryCache . create ( cacheBasename , cacheDirname )
152+ : null
153+
125154 const extractFromFile = async ( file : string ) => {
126155 const babelOpts = {
127156 presets : resolvePresets ( presets , babelrcDir ) ,
@@ -131,7 +160,6 @@ export default async (
131160 const localeObj = localeMap ( locales )
132161 const result = metadata [ 'react-intl' ] . messages as Message [ ]
133162 for ( const { id, defaultMessage, description } of result ) {
134- // eslint-disable-next-line no-unused-vars
135163 for ( const locale of locales ) {
136164 const message = defaultLocale === locale ? defaultMessage : ''
137165 localeObj [ locale ] [ id ] = withDescriptions
@@ -142,10 +170,36 @@ export default async (
142170 return localeObj
143171 }
144172
145- const files : string [ ] = await pify ( glob ) ( pattern )
146- if ( files . length === 0 ) {
147- throw new Error ( `File not found (${ pattern } )` )
173+ const extractFromCache = async ( file : string ) => {
174+ if ( cache === null ) {
175+ return extractFromFile ( file )
176+ }
177+
178+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
179+ const cachedLocaleObj = cache ?. getFileDescriptor ( file ) as File | undefined
180+ const changed = cachedLocaleObj ?. changed
181+ const data = cachedLocaleObj ?. meta . data
182+
183+ if ( changed === false && data ?. defaultLocale === defaultLocale ) {
184+ return data . localeMap
185+ }
186+
187+ const localeObj = await extractFromFile ( file )
188+
189+ if ( cachedLocaleObj ) {
190+ cachedLocaleObj . meta . data = { defaultLocale, localeMap : localeObj }
191+ }
192+
193+ return localeObj
148194 }
149- const arr = await Promise . all ( files . map ( extractFromFile ) )
195+
196+ const extract = cacheEnabled ? extractFromCache : extractFromFile
197+
198+ const arr = await Promise . all ( files . map ( extract ) )
199+
200+ if ( cache ) {
201+ cache . reconcile ( )
202+ }
203+
150204 return arr . reduce ( ( h , obj ) => merge ( h , obj ) , localeMap ( locales ) )
151205}
0 commit comments