@@ -6,6 +6,7 @@ import type {
66} from "../types/index.js" ;
77import { isFunction , stripBom } from "../utils/index.js" ;
88import { createLexerConfig , Lexer , type Token , TokenType } from "./lexer.js" ;
9+ import { parseDynamic } from "../heuristics/dynamic-typing.js" ;
910
1011/**
1112 * Parser state for row assembly and processing
@@ -55,6 +56,141 @@ export class Parser implements PapaParseParser {
5556 this . state = this . createInitialState ( ) ;
5657 }
5758
59+ /**
60+ * Fast mode parsing - bypasses lexer for performance
61+ * Mirrors legacy lines 1482-1513 exactly
62+ */
63+ private parseFastMode (
64+ input : string ,
65+ baseIndex : number ,
66+ ignoreLastRow : boolean ,
67+ ) : PapaParseResult {
68+ const newline =
69+ ( this . config . newline as string ) || this . guessLineEndings ( input ) || "\r\n" ;
70+ const delimiter = ( this . config . delimiter as string ) || "," ;
71+ const comments =
72+ typeof this . config . comments === "string" ? this . config . comments : false ;
73+ const commentsLen = comments ? comments . length : 0 ;
74+
75+ const rows = input . split ( newline ) ;
76+ const data : any [ ] [ ] = [ ] ;
77+
78+ for ( let i = 0 ; i < rows . length ; i ++ ) {
79+ const row = rows [ i ] ;
80+
81+ if ( i === rows . length - 1 && ignoreLastRow ) {
82+ break ;
83+ }
84+
85+ // Skip comment lines
86+ if ( comments && row . substring ( 0 , commentsLen ) === comments ) {
87+ continue ;
88+ }
89+
90+ // Split row by delimiter and add to data
91+ let fields = row . split ( delimiter ) ;
92+
93+ // Apply dynamic typing and transforms
94+ if ( this . config . dynamicTyping || this . config . transform ) {
95+ fields = fields . map ( ( field , index ) => {
96+ let value = field ;
97+
98+ // Apply transform if provided
99+ if (
100+ this . config . transform &&
101+ typeof this . config . transform === "function"
102+ ) {
103+ value = this . config . transform ( value , index ) ;
104+ }
105+
106+ // Apply dynamic typing
107+ if ( this . config . dynamicTyping ) {
108+ value = parseDynamic (
109+ value ,
110+ String ( index ) ,
111+ this . config . dynamicTyping as any ,
112+ ) ;
113+ }
114+
115+ return value ;
116+ } ) ;
117+ }
118+
119+ data . push ( fields ) ;
120+
121+ // Handle preview limit
122+ if ( this . config . preview && data . length >= this . config . preview ) {
123+ break ;
124+ }
125+ }
126+
127+ // Apply header processing and transformations
128+ return this . buildFastModeResult ( data , baseIndex ) ;
129+ }
130+
131+ /**
132+ * Build result for fast mode parsing
133+ */
134+ private buildFastModeResult (
135+ data : any [ ] [ ] ,
136+ baseIndex : number ,
137+ ) : PapaParseResult {
138+ const newline = ( this . config . newline as string ) || "\r\n" ;
139+ const delimiter = ( this . config . delimiter as string ) || "," ;
140+ // Apply header logic if needed
141+ if ( this . config . header && data . length > 0 ) {
142+ const headers = data [ 0 ] ;
143+ const rows = data . slice ( 1 ) ;
144+
145+ // Convert to objects
146+ const objectData = rows . map ( ( row ) => {
147+ const obj : any = { } ;
148+ headers . forEach ( ( header , index ) => {
149+ obj [ header ] = row [ index ] || "" ;
150+ } ) ;
151+ return obj ;
152+ } ) ;
153+
154+ return {
155+ data : objectData ,
156+ errors : [ ] ,
157+ meta : {
158+ delimiter : delimiter ,
159+ linebreak : newline ,
160+ aborted : false ,
161+ truncated : false ,
162+ cursor : baseIndex + data . length ,
163+ } ,
164+ } ;
165+ }
166+
167+ return {
168+ data,
169+ errors : [ ] ,
170+ meta : {
171+ delimiter : delimiter ,
172+ linebreak : newline ,
173+ aborted : false ,
174+ truncated : false ,
175+ cursor : baseIndex + data . length ,
176+ } ,
177+ } ;
178+ }
179+
180+ /**
181+ * Guess line endings from input
182+ */
183+ private guessLineEndings ( input : string ) : string | null {
184+ const crCount = ( input . match ( / \r / g) || [ ] ) . length ;
185+ const lfCount = ( input . match ( / \n / g) || [ ] ) . length ;
186+ const crlfCount = ( input . match ( / \r \n / g) || [ ] ) . length ;
187+
188+ if ( crlfCount > 0 ) return "\r\n" ;
189+ if ( lfCount > crCount ) return "\n" ;
190+ if ( crCount > 0 ) return "\r" ;
191+ return null ;
192+ }
193+
58194 /**
59195 * Parse input string and return results
60196 * Legacy reference: lines 1461-1806
@@ -72,6 +208,16 @@ export class Parser implements PapaParseParser {
72208 return this . buildResult ( baseIndex ) ;
73209 }
74210
211+ // Fast mode bypass - mirror legacy behavior exactly (lines 1482-1513)
212+ const canUseFastMode =
213+ this . config . fastMode === true ||
214+ ( this . config . fastMode !== false &&
215+ input . indexOf ( this . config . quoteChar || '"' ) === - 1 ) ;
216+
217+ if ( canUseFastMode && ! this . config . step && ! this . config . chunk ) {
218+ return this . parseFastMode ( input , baseIndex , ignoreLastRow ) ;
219+ }
220+
75221 // Tokenize input
76222 const { tokens, errors, terminatedByComment } = this . lexer . tokenize ( ) ;
77223 this . state . errors . push ( ...errors ) ;
0 commit comments