1818
1919import { syllable as syllableEn } from 'syllable' ;
2020
21+ // Static imports for hyphenation patterns (required for Lambda bundling)
22+ import hyphenDe from 'hyphen/de/index.js' ;
23+ import hyphenEs from 'hyphen/es/index.js' ;
24+ import hyphenIt from 'hyphen/it/index.js' ;
25+ import hyphenFr from 'hyphen/fr/index.js' ;
26+ import hyphenNl from 'hyphen/nl/index.js' ;
27+
28+ // Map of locale to statically imported hyphenation module
29+ const HYPHEN_MODULES = Object . freeze ( {
30+ de : hyphenDe ,
31+ es : hyphenEs ,
32+ it : hyphenIt ,
33+ fr : hyphenFr ,
34+ nl : hyphenNl ,
35+ } ) ;
36+
2137export const SUPPORTED_LANGUAGES = {
2238 eng : 'english' ,
2339 deu : 'german' ,
@@ -56,41 +72,34 @@ const COEFFS = Object.freeze({
5672 english : { A : 206.835 , wps : 1.015 , spw : 84.6 } ,
5773} ) ;
5874
59- // Optional: in-flight promise cache to dedupe concurrent analyze() calls
60- const syllablePromiseCache = new Map ( ) ; // key -> Promise<number>
61-
6275const clamp = ( x ) => Math . max ( 0 , Math . min ( 100 , x ) ) ;
6376
64- // --- Hyphenation loader (lazy ) ---
65- // cache promises to dedupe concurrent calls
66- const hyphenatorCache = new Map ( ) ; // Map<string, Promise< Function|null> >
77+ // --- Hyphenation loader (using static imports ) ---
78+ // cache to store resolved hyphenators
79+ const hyphenatorCache = new Map ( ) ; // Map<string, Function|null>
6780
68- export async function getHyphenator ( language ) {
81+ export function getHyphenator ( language ) {
6982 const key = String ( language ) . toLowerCase ( ) ;
7083 const cached = hyphenatorCache . get ( key ) ;
71- if ( cached ) return cached ;
72-
73- const loader = ( async ( ) => {
74- const locale = LOCALE_MAP [ key ] ;
75- if ( ! locale ) {
76- // eslint-disable-next-line no-console
77- console . info ( `[readability-suggest multilingual] 🔤 No hyphenation patterns available for language: ${ language } ` ) ;
78- return null ;
79- }
84+ if ( cached !== undefined ) return cached ;
8085
86+ const locale = LOCALE_MAP [ key ] ;
87+ if ( ! locale ) {
8188 // eslint-disable-next-line no-console
82- console . info ( `[readability-suggest multilingual] 📚 Loading hyphenation patterns for ${ language } (locale: ${ locale } )` ) ;
83- const mod = await import ( `hyphen/${ locale } /index.js` ) ;
84- const hyphenate = mod ?. default ?. hyphenate ;
85- if ( hyphenate ) {
86- // eslint-disable-next-line no-console
87- console . info ( `[readability-suggest multilingual] ✅ Successfully loaded hyphenation patterns for ${ language } ` ) ;
88- }
89- return hyphenate ;
90- } ) ( ) ;
89+ console . info ( `[readability-suggest multilingual] 🔤 No hyphenation patterns available for language: ${ language } ` ) ;
90+ hyphenatorCache . set ( key , null ) ;
91+ return null ;
92+ }
9193
92- hyphenatorCache . set ( key , loader ) ;
93- return loader ;
94+ const mod = HYPHEN_MODULES [ locale ] ;
95+ // Use hyphenateSync for better performance (no async overhead)
96+ const hyphenate = mod ?. hyphenateSync ;
97+ if ( hyphenate ) {
98+ // eslint-disable-next-line no-console
99+ console . info ( `[readability-suggest multilingual] ✅ Loaded hyphenation patterns for ${ language } ` ) ;
100+ }
101+ hyphenatorCache . set ( key , hyphenate || null ) ;
102+ return hyphenate || null ;
94103}
95104
96105// --- Tokenization (streaming, locale-aware) ---
@@ -151,12 +160,12 @@ const defaultComplexThreshold = 3;
151160 * Count syllables for a single word in a given language.
152161 * EN uses "syllable"; others use hyphenation splits as proxy.
153162 */
154- async function countSyllablesWord ( word , language ) {
163+ function countSyllablesWord ( word , language ) {
155164 const lang = language . toLowerCase ( ) ;
156165 if ( lang === 'english' ) {
157166 return Math . max ( 1 , syllableEn ( word ) ) ;
158167 }
159- const hyphenate = await getHyphenator ( lang ) ;
168+ const hyphenate = getHyphenator ( lang ) ;
160169 if ( ! hyphenate ) {
161170 // generic Unicode vowel group fallback
162171 const m = word . toLowerCase ( ) . match ( / [ a e i o u y à - ɏ ] + / giu) ;
@@ -172,7 +181,7 @@ async function countSyllablesWord(word, language) {
172181 }
173182 // Preserve inner apostrophes/dashes, remove other junk
174183 const cleaned = word . replace ( / [ ^ \p{ L} \p{ M} ' ' - ] / gu, '' ) ;
175- const hyphenatedString = await hyphenate ( cleaned ) ;
184+ const hyphenatedString = hyphenate ( cleaned ) ;
176185 // Split by soft hyphen character (U+00AD) to get syllable parts
177186 const syllableParts = hyphenatedString ? hyphenatedString . split ( '\u00AD' ) : [ word ] ;
178187 const syllableCount = Math . max ( 1 , syllableParts . length ) ;
@@ -251,41 +260,16 @@ export async function analyzeReadability(text, language, opts = {}) {
251260 else entries . set ( key , { word : w , count : 1 } ) ;
252261 }
253262
254- // 2) Resolve syllables per unique word, using cache and deduped promises
255- const toResolve = [ ] ;
263+ // 2) Resolve syllables per unique word, using cache
256264 for ( const [ key , { word } ] of entries ) {
257265 if ( cache . get ( key ) == null ) {
258- let p = syllablePromiseCache . get ( key ) ;
259- if ( ! p ) {
260- p = countSyllablesWord ( word , lang ) . then ( ( n ) => {
261- cache . set ( key , n ) ;
262- syllablePromiseCache . delete ( key ) ; // keep cache small
263- return n ;
264- } ) ;
265- syllablePromiseCache . set ( key , p ) ;
266- }
267- toResolve . push ( p ) ;
266+ cache . set ( key , countSyllablesWord ( word , lang ) ) ;
268267 }
269268 }
270- if ( toResolve . length ) await Promise . all ( toResolve ) ;
271269
272270 // 3) Aggregate syllables/complex words using frequencies
273271 let syllableCount = 0 ;
274272 let complexWords = 0 ;
275- // CRITICAL FIX: Check for corrupted cache entries and recalculate if needed
276- const toRecalculate = [ ] ;
277- for ( const [ key , { word } ] of entries ) {
278- const s = cache . get ( key ) ;
279- // If cache returns undefined/null or 0 syllables, need to recalculate
280- if ( s === undefined || s === null || s === 0 ) {
281- toRecalculate . push ( countSyllablesWord ( word , lang ) . then ( ( n ) => {
282- cache . set ( key , n ) ;
283- return n ;
284- } ) ) ;
285- }
286- }
287- if ( toRecalculate . length ) await Promise . all ( toRecalculate ) ;
288-
289273 for ( const [ key , { count } ] of entries ) {
290274 const s = cache . get ( key ) ?? 0 ;
291275 syllableCount += s * count ;
0 commit comments