@@ -2,12 +2,14 @@ import fs from 'fs/promises';
22import path from 'path' ;
33import { beforeEach , describe , expect , it , vi } from 'vitest' ;
44import ExtractionCompiler from './ExtractionCompiler.js' ;
5- import SourceFileScanner from './source/SourceFileScanner.js' ;
65
76const filesystem : {
87 project : {
98 src : Record < string , string > ;
109 messages : Record < string , string > | undefined ;
10+ node_modules ?: Record < '@acme' , Record < 'ui' , Record < string , string > > > ;
11+ '.next' ?: Record < string , Record < string , string > > ;
12+ '.git' ?: Record < string , Record < string , string > > ;
1113 } ;
1214} = {
1315 project : {
@@ -16,19 +18,19 @@ const filesystem: {
1618 }
1719} ;
1820
19- describe ( 'json format' , ( ) => {
20- beforeEach ( ( ) => {
21- filesystem . project = {
22- src : { } ,
23- messages : { }
24- } ;
25- delete ( filesystem as Record < string , unknown > ) . ui ;
26- fileTimestamps . clear ( ) ;
27- watchCallbacks . clear ( ) ;
28- mockWatchers . clear ( ) ;
29- vi . clearAllMocks ( ) ;
30- } ) ;
21+ beforeEach ( ( ) => {
22+ filesystem . project = {
23+ src : { } ,
24+ messages : { }
25+ } ;
26+ delete ( filesystem as Record < string , unknown > ) . ui ;
27+ fileTimestamps . clear ( ) ;
28+ watchCallbacks . clear ( ) ;
29+ mockWatchers . clear ( ) ;
30+ vi . clearAllMocks ( ) ;
31+ } ) ;
3132
33+ describe ( 'json format' , ( ) => {
3234 function createCompiler ( ) {
3335 return new ExtractionCompiler (
3436 {
@@ -314,28 +316,28 @@ describe('json format', () => {
314316 `
315317 ) ;
316318 expect ( filesystem ) . toMatchInlineSnapshot ( `
317- {
318- "project": {
319- "messages": {
320- "de.json": "{
321- "+YJVTi": "Hallo!"
322- }",
323- "en.json": "{
324- "+YJVTi": "Hey!"
325- }",
326- },
327- "src": {
328- "Greeting.tsx": "
329- import {useExtracted} from 'next-intl';
330- function Greeting() {
331- const t = useExtracted();
332- return <div>{t('Hey!')}</div>;
333- }
334- ",
319+ {
320+ "project": {
321+ "messages": {
322+ "de.json": "{
323+ "+YJVTi": "Hallo!"
324+ }",
325+ "en.json": "{
326+ "+YJVTi": "Hey!"
327+ }",
328+ },
329+ "src": {
330+ "Greeting.tsx": "
331+ import {useExtracted} from 'next-intl';
332+ function Greeting() {
333+ const t = useExtracted();
334+ return <div>{t('Hey!')}</div>;
335+ }
336+ ",
337+ },
335338 },
336- },
337- }
338- ` ) ;
339+ }
340+ ` ) ;
339341
340342 simulateManualFileEdit (
341343 'messages/de.json' ,
@@ -696,18 +698,6 @@ describe('json format', () => {
696698} ) ;
697699
698700describe ( 'po format' , ( ) => {
699- beforeEach ( ( ) => {
700- filesystem . project = {
701- src : { } ,
702- messages : { }
703- } ;
704- delete ( filesystem as Record < string , unknown > ) . ui ;
705- fileTimestamps . clear ( ) ;
706- watchCallbacks . clear ( ) ;
707- mockWatchers . clear ( ) ;
708- vi . clearAllMocks ( ) ;
709- } ) ;
710-
711701 function createCompiler ( ) {
712702 return new ExtractionCompiler (
713703 {
@@ -1159,83 +1149,103 @@ msgstr "Hallo!"
11591149 } ) ;
11601150} ) ;
11611151
1162- describe ( 'SourceFileScanner filtering' , ( ) => {
1152+ describe ( '`srcPath` filtering' , ( ) => {
11631153 beforeEach ( ( ) => {
1164- filesystem . project = {
1165- src : { } ,
1166- messages : { }
1167- } ;
1168- delete ( filesystem as Record < string , unknown > ) . ui ;
1169- fileTimestamps . clear ( ) ;
1170- watchCallbacks . clear ( ) ;
1171- mockWatchers . clear ( ) ;
1172- vi . clearAllMocks ( ) ;
1173- } ) ;
1154+ filesystem . project . src [ 'Greeting.tsx' ] = `
1155+ import {useExtracted} from 'next-intl';
1156+ import Panel from '@acme/ui/panel';
1157+ function Greeting() {
1158+ const t = useExtracted();
1159+ return <Panel>{t('Hey!')}</Panel>;
1160+ }
1161+ ` ;
11741162
1175- it ( 'skips node_modules unless requested' , async ( ) => {
1176- ( filesystem . project . src as Record < string , string > ) [ 'App.tsx' ] =
1177- 'export default 1;' ;
1178- ( filesystem . project as Record < string , unknown > ) . node_modules = {
1179- '@acme' : {
1180- 'design-system' : {
1181- 'Button.tsx' : 'export default 1;'
1182- }
1163+ function createNodeModule ( moduleName : string ) {
1164+ return `
1165+ import {useExtracted} from 'next-intl';
1166+ export default function Module({children}) {
1167+ const t = useExtracted();
1168+ return (
1169+ <div>
1170+ <h1>{t('${ moduleName } ')}</h1>
1171+ {children}
1172+ </div>
1173+ )
11831174 }
1184- } ;
1185-
1186- const files = await SourceFileScanner . getSourceFiles ( [ path . join ( '/project' ) ] ) ;
1187-
1188- expect ( files ) . toContain ( path . join ( '/project' , 'src' , 'App.tsx' ) ) ;
1189- expect ( files ) . not . toContain (
1190- path . join (
1191- '/project' ,
1192- 'node_modules' ,
1193- '@acme' ,
1194- 'design-system' ,
1195- 'Button.tsx'
1196- )
1197- ) ;
1198- } ) ;
1175+ ` ;
1176+ }
11991177
1200- it ( 'includes explicit node_modules paths' , async ( ) => {
1201- const designSystemDir = path . join (
1202- '/project' ,
1203- 'node_modules' ,
1204- '@acme' ,
1205- 'design-system'
1206- ) ;
1207- ( filesystem . project as Record < string , unknown > ) . node_modules = {
1178+ filesystem . project . node_modules = {
12081179 '@acme' : {
1209- 'design-system' : {
1210- 'Button .tsx' : 'export default 1;'
1180+ ui : {
1181+ 'panel .tsx' : createNodeModule ( 'panel.source' )
12111182 }
12121183 }
12131184 } ;
1214-
1215- const files = await SourceFileScanner . getSourceFiles ( [ designSystemDir ] ) ;
1216-
1217- expect ( files ) . toEqual ( [ path . join ( designSystemDir , 'Button.tsx' ) ] ) ;
1185+ filesystem . project [ '.next' ] = {
1186+ build : {
1187+ 'panel.tsx' : createNodeModule ( 'panel.compiled' )
1188+ }
1189+ } ;
1190+ filesystem . project [ '.git' ] = {
1191+ config : {
1192+ 'panel.tsx' : createNodeModule ( 'panel.config' )
1193+ }
1194+ } ;
12181195 } ) ;
12191196
1220- it ( 'skips node_modules in external directories' , async ( ) => {
1221- const externalRoot = '/ui' ;
1222- ( filesystem as Record < string , unknown > ) . ui = {
1223- 'index.tsx' : 'export default 1;' ,
1224- node_modules : {
1225- '@scope' : {
1226- 'Widget.tsx' : 'export default 1;'
1197+ function createCompiler ( srcPath : string | Array < string > ) {
1198+ return new ExtractionCompiler (
1199+ {
1200+ srcPath,
1201+ sourceLocale : 'en' ,
1202+ messages : {
1203+ path : './messages' ,
1204+ format : 'json' ,
1205+ locales : 'infer'
12271206 }
1228- }
1229- } ;
1207+ } ,
1208+ { isDevelopment : true , projectRoot : '/project' }
1209+ ) ;
1210+ }
12301211
1231- const files = await SourceFileScanner . getSourceFiles ( [
1232- path . join ( '/project' , '../ui' )
1233- ] ) ;
1212+ it ( 'skips node_modules, .next and .git by default' , async ( ) => {
1213+ using compiler = createCompiler ( './' ) ;
1214+ await compiler . compile (
1215+ '/project/src/Greeting.tsx' ,
1216+ filesystem . project . src [ 'Greeting.tsx' ]
1217+ ) ;
1218+ await waitForWriteFileCalls ( 1 ) ;
1219+ expect ( vi . mocked ( fs . writeFile ) . mock . calls ) . toMatchInlineSnapshot ( `
1220+ [
1221+ [
1222+ "messages/en.json",
1223+ "{
1224+ "+YJVTi": "Hey!"
1225+ }",
1226+ ],
1227+ ]
1228+ ` ) ;
1229+ } ) ;
12341230
1235- expect ( files ) . toContain ( path . join ( externalRoot , 'index.tsx' ) ) ;
1236- expect ( files ) . not . toContain (
1237- path . join ( externalRoot , 'node_modules' , '@scope' , 'Widget.tsx' )
1231+ it ( 'includes node_modules if explicitly requested' , async ( ) => {
1232+ using compiler = createCompiler ( [ './' , './node_modules/@acme/ui' ] ) ;
1233+ await compiler . compile (
1234+ '/project/src/Greeting.tsx' ,
1235+ filesystem . project . src [ 'Greeting.tsx' ]
12381236 ) ;
1237+ await waitForWriteFileCalls ( 1 ) ;
1238+ expect ( vi . mocked ( fs . writeFile ) . mock . calls ) . toMatchInlineSnapshot ( `
1239+ [
1240+ [
1241+ "messages/en.json",
1242+ "{
1243+ "JwjlWH": "panel.source",
1244+ "+YJVTi": "Hey!"
1245+ }",
1246+ ],
1247+ ]
1248+ ` ) ;
12391249 } ) ;
12401250} ) ;
12411251
0 commit comments