@@ -202,18 +202,141 @@ const stringifyJSON = (data) => {
202202 : json || '{}' ;
203203} ;
204204
205- const stringifyFn = ( fn ) => {
206- let value = fn . toString ( ) . replace ( / \n / g, '' ) ;
207- let isArrowFunction = value . indexOf ( '=>' , 1 ) > 0 ;
205+ /**
206+ * Removes all JavaScript comments (single-line and multi-line)
207+ * from the given source code, while preserving string and template literals.
208+ *
209+ * This function avoids removing comment-like patterns inside string literals
210+ * (single quotes, double quotes, or backticks), and skips escaped characters.
211+ *
212+ * @param {string } code The JavaScript source code to clean.
213+ * @returns {string } The code with all comments removed.
214+ *
215+ * @example
216+ * // Removes only real comments, not content inside strings:
217+ * const input = "const x = 'text // not a comment'; // real comment";
218+ * const output = stripComments(input);
219+ * // => "const x = 'text // not a comment'; "
220+ */
221+ function stripComments ( code ) {
222+ let out = '' ;
223+ let i = 0 ;
224+ const len = code . length ;
225+ let inStr = null ; // "'", '"', or '`'
226+ let inBlockComment = false ;
227+ let inLineComment = false ;
228+
229+ while ( i < len ) {
230+ const char = code [ i ] ;
231+ const next = code [ i + 1 ] ;
232+
233+ // end of line comment
234+ if ( inLineComment && ( char === '\n' || char === '\r' ) ) {
235+ inLineComment = false ;
236+ out += char ;
237+ i ++ ;
238+ continue ;
239+ }
240+
241+ // end of block comment
242+ if ( inBlockComment && char === '*' && next === '/' ) {
243+ inBlockComment = false ;
244+ i += 2 ;
245+ continue ;
246+ }
247+
248+ if ( inLineComment || inBlockComment ) {
249+ i ++ ;
250+ continue ;
251+ }
252+
253+ // handle string start
254+ if ( ! inStr && ( char === '"' || char === "'" || char === '`' ) ) {
255+ inStr = char ;
256+ out += char ;
257+ i ++ ;
258+ continue ;
259+ }
208260
209- if ( ! isArrowFunction ) {
210- const pos = value . indexOf ( '(' ) ;
211- if ( pos > 0 && value . slice ( 0 , pos ) . trim ( ) !== 'function' ) {
212- value = 'function' + value . slice ( pos ) ;
261+ // handle string end (skip escaped)
262+ if ( inStr ) {
263+ out += char ;
264+ if ( char === '\\' ) {
265+ out += code [ i + 1 ] ;
266+ i += 2 ;
267+ continue ;
268+ }
269+ if ( char === inStr ) {
270+ inStr = null ;
271+ }
272+ i ++ ;
273+ continue ;
213274 }
275+
276+ // handle line comment start
277+ if ( char === '/' && next === '/' ) {
278+ inLineComment = true ;
279+ i += 2 ;
280+ continue ;
281+ }
282+
283+ // handle block comment start
284+ if ( char === '/' && next === '*' ) {
285+ inBlockComment = true ;
286+ i += 2 ;
287+ continue ;
288+ }
289+
290+ out += char ;
291+ i ++ ;
214292 }
215293
216- return value ;
294+ return out ;
295+ }
296+
297+ /**
298+ * Stringify any JavaScript function and remove all comments.
299+ *
300+ * @param {Function } fn - The function to stringify.
301+ * @returns {string|null } The cleaned stringified function or null if not a function or native.
302+ */
303+ const stringifyFn = ( fn ) => {
304+ if ( typeof fn !== 'function' ) return null ;
305+
306+ try {
307+ const raw = fn . toString ( ) ;
308+
309+ // skip native or bound functions
310+ if ( raw . includes ( '[native code]' ) || raw . includes ( '[object Function]' ) ) {
311+ return null ;
312+ }
313+
314+ // safe comment removal
315+ let cleaned = stripComments ( raw ) ;
316+
317+ // remove leading indent and join into one line
318+ cleaned = cleaned
319+ . split ( '\n' )
320+ . map ( ( line ) => line . trimStart ( ) )
321+ . join ( ' ' )
322+ . trim ( ) ;
323+
324+ // check if it is top-level arrow function
325+ const isArrowFunction = / ^ ( \( ? [ ^ = ( ) { } ; ] * \) ? ) \s * = > / . test ( cleaned ) ;
326+
327+ // replace method shorthand to function expression
328+ // Example: getFoo(a, b) { ... } -> function(a, b) { ... }
329+ if ( ! isArrowFunction ) {
330+ const pos = cleaned . indexOf ( '(' ) ;
331+ if ( pos > 0 && cleaned . slice ( 0 , pos ) . trim ( ) !== 'function' ) {
332+ cleaned = 'function' + cleaned . slice ( pos ) ;
333+ }
334+ }
335+
336+ return cleaned ;
337+ } catch {
338+ return null ;
339+ }
217340} ;
218341
219342module . exports = {
0 commit comments