1

I am working on a problem where I need to ensure whether a particular argument is present in calls to console.log throughout in my code base. Simply speaking the main objective would be add that argument if not present.

In my initial approach, I am extracting the list of parameters to console.log(...) and thinking of making it into any array somehow (I am not sure how. I wish to make it reliable enough like closing braces, commas can be inside a string as a parameter to function etc.).

A complex instance would be

console.log("this is a string,another()",'single \'quote\'',param1, param['key'+some_variable]);

So any approach or suggestion to help me going.

Gautam Kumar Samal
  • 758
  • 1
  • 7
  • 23
  • Could you please clarify: the input is `var s = 'console.log("this is a string,another()",\'single \'quote\'\',param1, param[\'key\'+some_variable]);'`? See https://jsfiddle.net/2L1dtzgh/ – Wiktor Stribiżew Sep 05 '16 at 07:10
  • @WiktorStribiżew - This is amazing and correct according to my requirement. Are you sure if this would hold correct for all cases ? I got one case to worry i.e. if my first param is "this is string ); contains closing braces of console.log". Any hint ? – Gautam Kumar Samal Sep 05 '16 at 07:37
  • I am not sure what you mean by the edge case. Can you have `console.log("this is string);` where `"this is string);` is the value you need? – Wiktor Stribiżew Sep 05 '16 at 07:38
  • more likely console.log("this is a string ); and it contains edge case.",param1); So I should get "this is a string ); and it contains edge case." as first param and param1 as second. Only thing is if we get the regex that determine console start/end within a string as a param. – Gautam Kumar Samal Sep 05 '16 at 07:47
  • The point is that the `console.log(` is removed from the start, and `);` from the end. With the above input, I get `[ "\"this is a string ); and it contains edge case.\"", "param1" ]` array. – Wiktor Stribiżew Sep 05 '16 at 07:50
  • You are right indeed. I misrepresented some stuff. I need to extract the whole console.log statement from a file to process with your solution. So I am using console.log\([^\)]+\); as regex. Hence this wouldn't work in the above case. That is what I am worried about. – Gautam Kumar Samal Sep 05 '16 at 07:54
  • *that argument if not present* What argument do you want to add? –  Sep 05 '16 at 07:55
  • You have not indicated you need to extract the `console.log(...);` from the larger string in the question. That will make the code a bit longer, since you cannot use a tempered greedy token, but you may use my regex to build the whole `console.log();` regex and then parse it with my solution. – Wiktor Stribiżew Sep 05 '16 at 07:56
  • @WiktorStribiżew - got your point. Thanks for helping out. – Gautam Kumar Samal Sep 05 '16 at 08:01
  • Actually, it is rather fragile to use the regex to actually get the `console.log` line of code from a larger code. You should think of parsing the code with some parser rather than a regex. – Wiktor Stribiżew Sep 05 '16 at 08:05
  • You should override `console.log` and check for the extra parameter you want at runtime. I would provide an example if you could tell me what argument you want to check for and add. –  Sep 05 '16 at 08:07
  • @torazaburo - I am in fact overriding console.log for a purpose but I need that extra parameter from the declaration. For this I am going through the trouble to ensure that the extra param is present in every console. – Gautam Kumar Samal Sep 05 '16 at 08:09
  • Which extra parameter? By "declaration" do you mean "invocation"? Show the desired result based on your sample code. –  Sep 05 '16 at 08:10
  • Yes and It's somewhat related to context and there is no alternatives. Thanks. – Gautam Kumar Samal Sep 05 '16 at 08:11
  • If it's related to context then how are you going to know what parameter to add even after you've parsed the call? What are you going to do with the elements in the array that Wiktor's regexp gives you? There are no alternatives to what? –  Sep 05 '16 at 08:12
  • I am gonna add a parameter like (param ? param : null) and I know most section of my invoking methods would have the same variable defined. – Gautam Kumar Samal Sep 05 '16 at 08:43
  • How many of these do you need to fix? You're best off just grepping through your source for `console.log` and fixing them manually. –  Sep 05 '16 at 11:55

1 Answers1

0

NOTE: The code and regex patterns below are good to use for parsing a single console.log line as input. When you need to parse the whole code, you need to check JavaScript parser in JavaScript.

You can use a 2 step approach: remove the console.log( from the start and ); from the end with a replace, and then use match with a regex to extract each single argument that can be either a double- or single-quoted string literal or a plain variable with no comma inside.

You may use

var s = 'console.log(/s[\\\\\\/]/g,a/b,`${`foobar, and more foobar`}`,"this is a string,another()",\'single \\\'quote\\\'\',param1, param[\'key\'+some_variable],`"\\` and more`);';
console.log(s.replace(/^console\.log\(|\);$/g,'').match(/\/(?:\\.|[^\\\/])+\/[gim]*|`\${`[^`\\]*(?:\\.[^\\`]*)*`}`|(?:"[^"\\]*(?:\\.[^\\"]*)*"|'[^'\\]*(?:\\.[^\\']*)*'|`[^`\\]*(?:\\.[^\\`]*)*`|[^,])+/g));

The main pattern to parse the string inside console.log() is

/\/(?:\\.|[^\\\/])+\/[gim]*|`\${`[^`\\]*(?:\\.[^\\`]*)*`}`|(?:"[^"\\]*(?:\\.[^\\"]*)*"|'[^'\\]*(?:\\.[^\\']*)*'|`[^`\\]*(?:\\.[^\\`]*)*`|[^,])+/g

See the regex demo.

Pattern details:

  • ^console\.log\( - console.log( string at the string start
  • | - or
  • \);$ - ); at the string end

and

  • \/(?:\\.|[^\\\/])+\/[gim]* - attempts to match a regex literal (those that start with /, then may have 1 or more non-/ or \+any char and can have any g, i, or m combinations after the trailing /
  • `\${`[^`\\]*(?:\\.[^\\`]*)*`}` - matches specific template string syntax
  • (?:"[^"\\]*(?:\\.[^\\"]*)*"|'[^'\\]*(?:\\.[^\\']*)*'|[^,'"]+)+ - 1 or more sequences of:
    • "[^"\\]*(?:\\.[^\\"]*)*" - a " followed with 0+ chars other than \ and ", followed with 0+ sequences of a \ + any char but a newline followed with 0+ chars other than \ and " (a double quoted literal pattern)
    • |'[^'\\]*(?:\\.[^\\']*)*' - or the similar pattern as above but with single quotes
    • |`[^`\\]*(?:\\.[^\\`]*)*` - a template string literal pattern similar to the above
    • |[^,'"]+ - or 1 or more chars other than ,, ", '.
Community
  • 1
  • 1
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • This is mind-bogglingly horrible. Are you going to move on now to write a JS parser in regexp? –  Sep 05 '16 at 07:56
  • @torazaburo: No, that is not the point, this is a good regex following the unroll-the-loop technique and it can parse the singl- or double-quoted string literals well in a well-formed string. I just did not consider that the `console.log();` might appear inside a longer text. Then, it makes little sense to use a regex to parse the whole code, sure. – Wiktor Stribiżew Sep 05 '16 at 08:02
  • This fails if the parameter is `/,/`. –  Sep 05 '16 at 10:37
  • It also fails on a template string containing a quote mark. –  Sep 05 '16 at 10:43
  • Hi @torazaburo: As for the regex literal, agreed, I added a disclaimer. I do not get the last comment about the quote mark though - it does match if there is a `console.log('"');` input – Wiktor Stribiżew Sep 05 '16 at 11:15
  • I applaud your virtuosity in crafting this regexp, and there are good lessons to be learned from it. So thanks. But it's a little bit odd to give an answer which doesn't really solve the OP's problem (apparently to fix **all** the `console.log`s in his program, not just match one), and still doesn't work in all cases, although one could say it solves it in the sense that it works for the cases he gave. –  Sep 05 '16 at 11:29
  • I was trying to say that a parameter of the form `\`"\`` doesn't work. In other words, a template string, delimited by backticks. –  Sep 05 '16 at 11:30
  • @torazaburo: Well, at leat template string literals are syntactically similar to other literal types. I added it as an alternative branch. I can actually think of adding the regex literals, too, but just do not have much time for it now. BTW, I posted this only because OP said it worked for OP. – Wiktor Stribiżew Sep 05 '16 at 11:39
  • Unfortunately, you have run out of gas in trying to parse JS with regexps, because the following is a valid template string which your new logic won't handle properly: `\`${\`foobar\`}\``. –  Sep 05 '16 at 11:41
  • Well, it does not matter what I will finally come to, I will update this answer in the future. – Wiktor Stribiżew Sep 05 '16 at 11:44
  • @torazaburo: I actually updated the regex since [JSLint](http://www.jslint.com/) looks unable to parse the template string literal provided by you last. I tried with `console.log(/s[\\\/]/g,a/b, \`${\`foobar\`}\`, \`${\`foobar, and more foobar\`}\`, "this is a string,another()", 'single \'quote\'',param1, param['key'+some_variable],\`"\\` and more\`);` – Wiktor Stribiżew Sep 05 '16 at 22:01
  • That seems to be a bug in JSLint. Chrome, Babel, and TypeScript all parse it without complaining. I've reported it to the maintainer. For reference, the exact string I tried was `var x = \`${\`${1}\`}\`;`. –  Sep 06 '16 at 02:49