Repository: oppia/oppia

Issue:
Make pyjsparser linter messages clearer.
I am trying to get my code to pass lint checks so that I can do a push. I am running into the following error:

```
[FileDiff(status='M', name='core/templates/dev/head/pages/collection_editor/collection_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/exploration_editor/exploration_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/skill_editor/skill_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/story_editor/story_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/topic_editor/topic_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/tests/protractor/accessibility.js'),
 FileDiff(status='M', name='core/tests/protractor/learnerDashboard.js'),
 FileDiff(status='M', name='core/tests/protractor/userJourneys.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/AdminPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/CollectionEditorPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/CreatorDashboardPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorHistoryTab.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorMainTab.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorSettingsTab.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/general.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/waitFor.js')]

Files to lint in waitfor:
['core/templates/dev/head/pages/collection_editor/collection_editor_save_modal_directive.html',
 'core/templates/dev/head/pages/exploration_editor/exploration_save_modal_directive.html',
 'core/templates/dev/head/pages/skill_editor/skill_editor_save_modal_directive.html',
 'core/templates/dev/head/pages/story_editor/story_editor_save_modal_directive.html',
 'core/templates/dev/head/pages/topic_editor/topic_editor_save_modal_directive.html',
 'core/tests/protractor/accessibility.js',
 'core/tests/protractor/learnerDashboard.js',
 'core/tests/protractor/userJourneys.js',
 'core/tests/protractor_utils/AdminPage.js',
 'core/tests/protractor_utils/CollectionEditorPage.js',
 'core/tests/protractor_utils/CreatorDashboardPage.js',
 'core/tests/protractor_utils/ExplorationEditorHistoryTab.js',
 'core/tests/protractor_utils/ExplorationEditorMainTab.js',
 'core/tests/protractor_utils/ExplorationEditorPage.js',
 'core/tests/protractor_utils/ExplorationEditorSettingsTab.js',
 'core/tests/protractor_utils/general.js',
 'core/tests/protractor_utils/waitFor.js']


Starting directive scope check
----------------------------------------
Traceback (most recent call last):
  File "/.git/hooks/../../scripts/pre_commit_linter.py", line 1555, in <module>
    main()
  File "/.git/hooks/../../scripts/pre_commit_linter.py", line 1527, in main
    directive_scope_messages = _check_directive_scope(all_files)
  File "/.git/hooks/../../scripts/pre_commit_linter.py", line 1115, in _check_directive_scope
    parsed_nodes = parser.parse(content)['body']
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2863, in parse
    program = self.parseProgram();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2834, in parseProgram
    body = self.parseScriptBody()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2823, in parseScriptBody
    statement = self.parseStatementListItem();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1989, in parseStatementListItem
    return self.parseStatement();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2565, in parseStatement
    return self.parseVariableStatement(node);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2066, in parseVariableStatement
    declarations = self.parseVariableDeclarationList()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2054, in parseVariableDeclarationList
    lis.append(self.parseVariableDeclaration())
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2045, in parseVariableDeclaration
    init = self.isolateCoverGrammar(self.parseAssignmentExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1923, in parseAssignmentExpression
    expr = self.parseConditionalExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1824, in parseConditionalExpression
    expr = self.inheritCoverGrammar(self.parseBinaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1773, in parseBinaryExpression
    left = self.inheritCoverGrammar(self.parseUnaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1749, in parseUnaryExpression
    expr = self.parsePostfixExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1702, in parsePostfixExpression
    expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1638, in parseLeftHandSideExpressionAllowCall
    self.parseNewExpression if self.matchKeyword('new') else self.parsePrimaryExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1549, in parsePrimaryExpression
    return self.parseFunctionExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2782, in parseFunctionExpression
    body = self.parseFunctionSourceElements();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2630, in parseFunctionSourceElements
    body.append(self.parseStatementListItem())
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1989, in parseStatementListItem
    return self.parseStatement();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2571, in parseStatement
    expr = self.parseExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1955, in parseExpression
    expr = self.isolateCoverGrammar(self.parseAssignmentExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1946, in parseAssignmentExpression
    right = self.isolateCoverGrammar(self.parseAssignmentExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1923, in parseAssignmentExpression
    expr = self.parseConditionalExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1824, in parseConditionalExpression
    expr = self.inheritCoverGrammar(self.parseBinaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1773, in parseBinaryExpression
    left = self.inheritCoverGrammar(self.parseUnaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1749, in parseUnaryExpression
    expr = self.parsePostfixExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1702, in parsePostfixExpression
    expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1638, in parseLeftHandSideExpressionAllowCall
    self.parseNewExpression if self.matchKeyword('new') else self.parsePrimaryExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1549, in parsePrimaryExpression
    return self.parseFunctionExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2782, in parseFunctionExpression
    body = self.parseFunctionSourceElements();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2630, in parseFunctionSourceElements
    body.append(self.parseStatementListItem())
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1989, in parseStatementListItem
    return self.parseStatement();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2571, in parseStatement
    expr = self.parseExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1955, in parseExpression
    expr = self.isolateCoverGrammar(self.parseAssignmentExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1923, in parseAssignmentExpression
    expr = self.parseConditionalExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1824, in parseConditionalExpression
    expr = self.inheritCoverGrammar(self.parseBinaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1773, in parseBinaryExpression
    left = self.inheritCoverGrammar(self.parseUnaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1722, in parseUnaryExpression
    expr = self.parsePostfixExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1702, in parsePostfixExpression
    expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1648, in parseLeftHandSideExpressionAllowCall
    args = self.parseArguments();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1589, in parseArguments
    self.expectCommaSeparator()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1040, in expectCommaSeparator
    self.expect(',')
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1031, in expect
    self.throwUnexpectedToken(token)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1020, in throwUnexpectedToken
    raise self.unexpectedTokenError(token, message)
pyjsparser.pyjsparserdata.JsSyntaxError: Line 109: Unexpected string
Push failed, please correct the linting issues above
error: failed to push some refs to 'https://github.com/oppia/oppia'
```

There are some problems with this error message.

Firstly I have no idea what file is causing the issue.

Secondly the stack trace is too long and mostly irrelevant. (Though this is a more minor problem.)

It would be helpful if we could print out a more useful error message. At the moment I don't really have enough information to fix the issue, and will need to go through each file manually.
Make pyjsparser linter messages clearer.
I am trying to get my code to pass lint checks so that I can do a push. I am running into the following error:

```
[FileDiff(status='M', name='core/templates/dev/head/pages/collection_editor/collection_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/exploration_editor/exploration_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/skill_editor/skill_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/story_editor/story_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/templates/dev/head/pages/topic_editor/topic_editor_save_modal_directive.html'),
 FileDiff(status='M', name='core/tests/protractor/accessibility.js'),
 FileDiff(status='M', name='core/tests/protractor/learnerDashboard.js'),
 FileDiff(status='M', name='core/tests/protractor/userJourneys.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/AdminPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/CollectionEditorPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/CreatorDashboardPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorHistoryTab.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorMainTab.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorPage.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/ExplorationEditorSettingsTab.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/general.js'),
 FileDiff(status='M', name='core/tests/protractor_utils/waitFor.js')]

Files to lint in waitfor:
['core/templates/dev/head/pages/collection_editor/collection_editor_save_modal_directive.html',
 'core/templates/dev/head/pages/exploration_editor/exploration_save_modal_directive.html',
 'core/templates/dev/head/pages/skill_editor/skill_editor_save_modal_directive.html',
 'core/templates/dev/head/pages/story_editor/story_editor_save_modal_directive.html',
 'core/templates/dev/head/pages/topic_editor/topic_editor_save_modal_directive.html',
 'core/tests/protractor/accessibility.js',
 'core/tests/protractor/learnerDashboard.js',
 'core/tests/protractor/userJourneys.js',
 'core/tests/protractor_utils/AdminPage.js',
 'core/tests/protractor_utils/CollectionEditorPage.js',
 'core/tests/protractor_utils/CreatorDashboardPage.js',
 'core/tests/protractor_utils/ExplorationEditorHistoryTab.js',
 'core/tests/protractor_utils/ExplorationEditorMainTab.js',
 'core/tests/protractor_utils/ExplorationEditorPage.js',
 'core/tests/protractor_utils/ExplorationEditorSettingsTab.js',
 'core/tests/protractor_utils/general.js',
 'core/tests/protractor_utils/waitFor.js']


Starting directive scope check
----------------------------------------
Traceback (most recent call last):
  File "/.git/hooks/../../scripts/pre_commit_linter.py", line 1555, in <module>
    main()
  File "/.git/hooks/../../scripts/pre_commit_linter.py", line 1527, in main
    directive_scope_messages = _check_directive_scope(all_files)
  File "/.git/hooks/../../scripts/pre_commit_linter.py", line 1115, in _check_directive_scope
    parsed_nodes = parser.parse(content)['body']
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2863, in parse
    program = self.parseProgram();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2834, in parseProgram
    body = self.parseScriptBody()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2823, in parseScriptBody
    statement = self.parseStatementListItem();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1989, in parseStatementListItem
    return self.parseStatement();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2565, in parseStatement
    return self.parseVariableStatement(node);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2066, in parseVariableStatement
    declarations = self.parseVariableDeclarationList()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2054, in parseVariableDeclarationList
    lis.append(self.parseVariableDeclaration())
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2045, in parseVariableDeclaration
    init = self.isolateCoverGrammar(self.parseAssignmentExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1923, in parseAssignmentExpression
    expr = self.parseConditionalExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1824, in parseConditionalExpression
    expr = self.inheritCoverGrammar(self.parseBinaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1773, in parseBinaryExpression
    left = self.inheritCoverGrammar(self.parseUnaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1749, in parseUnaryExpression
    expr = self.parsePostfixExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1702, in parsePostfixExpression
    expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1638, in parseLeftHandSideExpressionAllowCall
    self.parseNewExpression if self.matchKeyword('new') else self.parsePrimaryExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1549, in parsePrimaryExpression
    return self.parseFunctionExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2782, in parseFunctionExpression
    body = self.parseFunctionSourceElements();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2630, in parseFunctionSourceElements
    body.append(self.parseStatementListItem())
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1989, in parseStatementListItem
    return self.parseStatement();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2571, in parseStatement
    expr = self.parseExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1955, in parseExpression
    expr = self.isolateCoverGrammar(self.parseAssignmentExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1946, in parseAssignmentExpression
    right = self.isolateCoverGrammar(self.parseAssignmentExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1923, in parseAssignmentExpression
    expr = self.parseConditionalExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1824, in parseConditionalExpression
    expr = self.inheritCoverGrammar(self.parseBinaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1773, in parseBinaryExpression
    left = self.inheritCoverGrammar(self.parseUnaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1749, in parseUnaryExpression
    expr = self.parsePostfixExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1702, in parsePostfixExpression
    expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1638, in parseLeftHandSideExpressionAllowCall
    self.parseNewExpression if self.matchKeyword('new') else self.parsePrimaryExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1549, in parsePrimaryExpression
    return self.parseFunctionExpression()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2782, in parseFunctionExpression
    body = self.parseFunctionSourceElements();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2630, in parseFunctionSourceElements
    body.append(self.parseStatementListItem())
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1989, in parseStatementListItem
    return self.parseStatement();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 2571, in parseStatement
    expr = self.parseExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1955, in parseExpression
    expr = self.isolateCoverGrammar(self.parseAssignmentExpression)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1131, in isolateCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1923, in parseAssignmentExpression
    expr = self.parseConditionalExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1824, in parseConditionalExpression
    expr = self.inheritCoverGrammar(self.parseBinaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1773, in parseBinaryExpression
    left = self.inheritCoverGrammar(self.parseUnaryExpression);
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1722, in parseUnaryExpression
    expr = self.parsePostfixExpression();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1702, in parsePostfixExpression
    expr = self.inheritCoverGrammar(self.parseLeftHandSideExpressionAllowCall)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1146, in inheritCoverGrammar
    result = parser()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1648, in parseLeftHandSideExpressionAllowCall
    args = self.parseArguments();
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1589, in parseArguments
    self.expectCommaSeparator()
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1040, in expectCommaSeparator
    self.expect(',')
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1031, in expect
    self.throwUnexpectedToken(token)
  File "_tools/pyjsparser-2.5.2/pyjsparser/parser.py", line 1020, in throwUnexpectedToken
    raise self.unexpectedTokenError(token, message)
pyjsparser.pyjsparserdata.JsSyntaxError: Line 109: Unexpected string
Push failed, please correct the linting issues above
error: failed to push some refs to 'https://github.com/oppia/oppia'
```

There are some problems with this error message.

Firstly I have no idea what file is causing the issue.

Secondly the stack trace is too long and mostly irrelevant. (Though this is a more minor problem.)

It would be helpful if we could print out a more useful error message. At the moment I don't really have enough information to fix the issue, and will need to go through each file manually.


Discussion:
/cc @kevinlee12 
Incidentally, I found the error by actually trying to run the e2e tests locally. It wasn't due to a directive scope check (which had confused me in the first place, because I was only modifying e2e test files and some HTML files). Instead, it was that, in this code snippet:

```
  this.navigateToSubscriptionDashboard = function() {
    waitFor.elementToBeClickable(
      subscriptionTab,         <----------
      'Subscription Dashboard tab takes too long to be clickable');
    subscriptionTab.click();
    general.waitForLoadingMessage();
  };
```

I had missed the terminal comma on the line with the arrow.

This might indicate that something is wrong with the code in this lint check.
Hi Sean, `pyjsparser`, as the name suggests, parses JS files as Python dictionaries. Therefore, whenever there is any syntactical error in a file, the parser is not able to parse the corresponding file. It also acts a JS file validator.

I agree regarding the message. We can change it to include the current filename it is processing. Also, I think the line number for this error is 109 as is pointed out by the last line of the stack trace:  
                 `pyjsparser.pyjsparserdata.JsSyntaxError: Line 109: Unexpected string`
and it is syntax error.

We can change the message to something like:
```bash
Processing: path/to/file ...

<Stack Trace>
```
Please let me know your suggestions!

Thanks.
I get these error messages too whenever I have an unresolved merge conflict in my JS files... yes yes I know bad branch management. I think it would be lovely if there is some sort of JS linters before _check_directive_scope!
Hi Viet, in case of a merge conflict, your file cannot be parsed. It looks something like:
```javascript
<<<<<<<
Changes made on the branch that is being merged into. In most cases,
this is the branch that I have currently checked out (i.e. HEAD).
|||||||
The common ancestor version.
=======
Changes made on the branch that is being merged in. This is often a 
feature/topic branch.
>>>>>>>
```
This is certainly _not_ a valid JS syntax, therefore the error. In general, please resolve all merge conflicts  before running the lint checks.
Thanks!
I don't particularly mind the specific format of the message. But I agree that the name of the file to fix should be really clear. I should be able to find it within one second of skimming.

I also think that this should not be flagged to the developer as a "directive scope" check -- that seems like it's actively misleading.
Okay, we can adjust the error messages accordingly so that there is no confusion. I will pick this up in a couple of days. Thanks!
/cc @kevinlee12 
Incidentally, I found the error by actually trying to run the e2e tests locally. It wasn't due to a directive scope check (which had confused me in the first place, because I was only modifying e2e test files and some HTML files). Instead, it was that, in this code snippet:

```
  this.navigateToSubscriptionDashboard = function() {
    waitFor.elementToBeClickable(
      subscriptionTab,         <----------
      'Subscription Dashboard tab takes too long to be clickable');
    subscriptionTab.click();
    general.waitForLoadingMessage();
  };
```

I had missed the terminal comma on the line with the arrow.

This might indicate that something is wrong with the code in this lint check.
Hi Sean, `pyjsparser`, as the name suggests, parses JS files as Python dictionaries. Therefore, whenever there is any syntactical error in a file, the parser is not able to parse the corresponding file. It also acts a JS file validator.

I agree regarding the message. We can change it to include the current filename it is processing. Also, I think the line number for this error is 109 as is pointed out by the last line of the stack trace:  
                 `pyjsparser.pyjsparserdata.JsSyntaxError: Line 109: Unexpected string`
and it is syntax error.

We can change the message to something like:
```bash
Processing: path/to/file ...

<Stack Trace>
```
Please let me know your suggestions!

Thanks.
I get these error messages too whenever I have an unresolved merge conflict in my JS files... yes yes I know bad branch management. I think it would be lovely if there is some sort of JS linters before _check_directive_scope!
Hi Viet, in case of a merge conflict, your file cannot be parsed. It looks something like:
```javascript
<<<<<<<
Changes made on the branch that is being merged into. In most cases,
this is the branch that I have currently checked out (i.e. HEAD).
|||||||
The common ancestor version.
=======
Changes made on the branch that is being merged in. This is often a 
feature/topic branch.
>>>>>>>
```
This is certainly _not_ a valid JS syntax, therefore the error. In general, please resolve all merge conflicts  before running the lint checks.
Thanks!
I don't particularly mind the specific format of the message. But I agree that the name of the file to fix should be really clear. I should be able to find it within one second of skimming.

I also think that this should not be flagged to the developer as a "directive scope" check -- that seems like it's actively misleading.
Okay, we can adjust the error messages accordingly so that there is no confusion. I will pick this up in a couple of days. Thanks!

Resolving patch:
diff --git a/scripts/pre_commit_linter.py b/scripts/pre_commit_linter.py
--- a/scripts/pre_commit_linter.py
+++ b/scripts/pre_commit_linter.py
@@ -1104,6 +1104,16 @@ def _check_html_directive_name(all_files):
     return summary_messages
 
 
+def _validate_and_parse_js_file(filename, content):
+    """This function validates a JavaScript file and returns the parsed contents
+    as a Python dictionary.
+    """
+    # Use Pyjsparser to parse a JS file as a Python dictionary.
+    parser = pyjsparser.PyJsParser()
+    print 'Validating and parsing %s file ...' % filename
+    return parser.parse(content)
+
+
 def _check_directive_scope(all_files):
     """This function checks that all directives have an explicit
     scope: {} and it should not be scope: true.
@@ -1117,13 +1127,12 @@ def _check_directive_scope(all_files):
         and filename.endswith('.js')]
     failed = False
     summary_messages = []
-    # Use Pyjsparser to parse a JS file as a Python dictionary.
-    parser = pyjsparser.PyJsParser()
     for filename in files_to_check:
         with open(filename) as f:
             content = f.read()
+        parsed_dict = _validate_and_parse_js_file(filename, content)
         # Parse the body of the content as nodes.
-        parsed_nodes = parser.parse(content)['body']
+        parsed_nodes = parsed_dict['body']
         for parsed_node in parsed_nodes:
             # Check the type of the node.
             if parsed_node['type'] != 'ExpressionStatement':


Test patch: