-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(gatsby): Add eslint rules to warn against bad patterns in pageTe…
…mplates (for Fast Refresh) (#28689) Co-authored-by: LekoArts <lekoarts@gmail.com> Co-authored-by: Peter van der Zee <209817+pvdz@users.noreply.github.com> Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com> Co-authored-by: gatsbybot <mathews.kyle+gatsbybot@gmail.com>
- Loading branch information
1 parent
b9978e1
commit 9a55d12
Showing
15 changed files
with
801 additions
and
8 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
...ts/development-runtime/cypress/integration/eslint-rules/limited-exports-page-templates.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
if (Cypress.env("HOT_LOADER") === `fast-refresh`) { | ||
describe(`limited-exports-page-templates`, () => { | ||
it(`should log warning to console for invalid export`, () => { | ||
cy.visit(`/eslint-rules/limited-exports-page-templates`, { | ||
onBeforeLoad(win) { | ||
cy.stub(win.console, 'log').as(`consoleLog`) | ||
} | ||
}).waitForRouteChange() | ||
|
||
cy.get(`@consoleLog`).should(`be.calledWithMatch`, /15:1 warning In page templates only a default export of a valid React component and the named export of a page query is allowed./i) | ||
cy.get(`@consoleLog`).should(`not.be.calledWithMatch`, /17:1 warning In page templates only a default export of a valid React component and the named export of a page query is allowed./i) | ||
}) | ||
}) | ||
} |
22 changes: 22 additions & 0 deletions
22
...velopment-runtime/cypress/integration/eslint-rules/no-anonymous-exports-page-templates.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
if (Cypress.env("HOT_LOADER") === `fast-refresh`) { | ||
describe(`no-anonymous-exports-page-templates`, () => { | ||
it(`should log warning to console for arrow functions`, () => { | ||
cy.visit(`/eslint-rules/no-anonymous-exports-page-templates`, { | ||
onBeforeLoad(win) { | ||
cy.stub(win.console, 'log').as(`consoleLog`) | ||
} | ||
}).waitForRouteChange() | ||
|
||
cy.get(`@consoleLog`).should(`be.calledWithMatch`, /Anonymous arrow functions cause Fast Refresh to not preserve local component state./i) | ||
}) | ||
it(`should log warning to console for function declarations`, () => { | ||
cy.visit(`/eslint-rules/no-anonymous-exports-page-templates-function`, { | ||
onBeforeLoad(win) { | ||
cy.stub(win.console, 'log').as(`consoleLog`) | ||
} | ||
}).waitForRouteChange() | ||
|
||
cy.get(`@consoleLog`).should(`be.calledWithMatch`, /Anonymous function declarations cause Fast Refresh to not preserve local component state./i) | ||
}) | ||
}) | ||
} |
27 changes: 27 additions & 0 deletions
27
e2e-tests/development-runtime/src/pages/eslint-rules/limited-exports-page-templates.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from "react" | ||
import { graphql } from "gatsby" | ||
|
||
function PageQuery({ data }) { | ||
return ( | ||
<div> | ||
<h1 data-testid="title">Limited Exports Page Templates. ESLint Rule</h1> | ||
<p data-testid="hot"> | ||
{data.site.siteMetadata.title} | ||
</p> | ||
</div> | ||
) | ||
} | ||
|
||
export function notAllowed() {} | ||
|
||
export const query = graphql` | ||
{ | ||
site { | ||
siteMetadata { | ||
title | ||
} | ||
} | ||
} | ||
` | ||
|
||
export default PageQuery |
11 changes: 11 additions & 0 deletions
11
...evelopment-runtime/src/pages/eslint-rules/no-anonymous-exports-page-templates-function.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from "react" | ||
|
||
import Layout from "../../components/layout" | ||
|
||
export default function () { | ||
return ( | ||
<Layout> | ||
<h1 data-testid="title">Anonymous Arrow Function. ESLint Rule</h1> | ||
</Layout> | ||
) | ||
} |
9 changes: 9 additions & 0 deletions
9
e2e-tests/development-runtime/src/pages/eslint-rules/no-anonymous-exports-page-templates.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React from "react" | ||
|
||
import Layout from "../../components/layout" | ||
|
||
export default () => ( | ||
<Layout> | ||
<h1 data-testid="title">Anonymous Arrow Function. ESLint Rule</h1> | ||
</Layout> | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import { mergeRequiredConfigIn } from "../eslint-config" | ||
import { CLIEngine } from "eslint" | ||
import * as path from "path" | ||
|
||
describe(`eslint-config`, () => { | ||
describe(`mergeRequiredConfigIn`, () => { | ||
it(`adds rulePaths and extends if those don't exist`, () => { | ||
const conf: CLIEngine.Options = {} | ||
|
||
mergeRequiredConfigIn(conf) | ||
|
||
expect(conf?.baseConfig).toMatchInlineSnapshot(` | ||
Object { | ||
"extends": Array [ | ||
"<PROJECT_ROOT>/packages/gatsby/src/utils/eslint/required.js", | ||
], | ||
} | ||
`) | ||
|
||
expect(conf.rulePaths).toMatchInlineSnapshot(` | ||
Array [ | ||
"<PROJECT_ROOT>/packages/gatsby/src/utils/eslint-rules", | ||
] | ||
`) | ||
}) | ||
|
||
it(`adds rulePath if rulePaths exist but don't contain required rules`, () => { | ||
const conf: CLIEngine.Options = { | ||
rulePaths: [`test`], | ||
} | ||
|
||
mergeRequiredConfigIn(conf) | ||
|
||
expect(conf.rulePaths).toMatchInlineSnapshot(` | ||
Array [ | ||
"test", | ||
"<PROJECT_ROOT>/packages/gatsby/src/utils/eslint-rules", | ||
] | ||
`) | ||
}) | ||
|
||
it(`doesn't add rulePath multiple times`, () => { | ||
const conf: CLIEngine.Options = { | ||
rulePaths: [path.resolve(__dirname, `../eslint-rules`), `test`], | ||
} | ||
|
||
mergeRequiredConfigIn(conf) | ||
|
||
expect(conf.rulePaths).toMatchInlineSnapshot(` | ||
Array [ | ||
"<PROJECT_ROOT>/packages/gatsby/src/utils/eslint-rules", | ||
"test", | ||
] | ||
`) | ||
}) | ||
|
||
it(`adds extend if extends exist (array) but don't contain required preset`, () => { | ||
const conf: CLIEngine.Options = { | ||
baseConfig: { | ||
extends: [`ext1`], | ||
}, | ||
} | ||
|
||
mergeRequiredConfigIn(conf) | ||
|
||
expect(conf.baseConfig).toMatchInlineSnapshot(` | ||
Object { | ||
"extends": Array [ | ||
"ext1", | ||
"<PROJECT_ROOT>/packages/gatsby/src/utils/eslint/required.js", | ||
], | ||
} | ||
`) | ||
}) | ||
|
||
it(`adds extend if extends exist (string) but don't contain required preset`, () => { | ||
const conf: CLIEngine.Options = { | ||
baseConfig: { | ||
extends: `ext1`, | ||
}, | ||
} | ||
|
||
mergeRequiredConfigIn(conf) | ||
|
||
expect(conf.baseConfig).toMatchInlineSnapshot(` | ||
Object { | ||
"extends": Array [ | ||
"ext1", | ||
"<PROJECT_ROOT>/packages/gatsby/src/utils/eslint/required.js", | ||
], | ||
} | ||
`) | ||
}) | ||
|
||
it(`doesn't add extend multiple times (extends is array)`, () => { | ||
const conf: CLIEngine.Options = { | ||
baseConfig: { | ||
extends: [require.resolve(`../eslint/required`), `ext1`], | ||
}, | ||
} | ||
|
||
mergeRequiredConfigIn(conf) | ||
|
||
expect(conf.baseConfig).toMatchInlineSnapshot(` | ||
Object { | ||
"extends": Array [ | ||
"<PROJECT_ROOT>/packages/gatsby/src/utils/eslint/required.js", | ||
"ext1", | ||
], | ||
} | ||
`) | ||
}) | ||
|
||
it(`doesn't add extend multiple times (extends is string)`, () => { | ||
const conf: CLIEngine.Options = { | ||
baseConfig: { | ||
extends: require.resolve(`../eslint/required`), | ||
}, | ||
} | ||
|
||
mergeRequiredConfigIn(conf) | ||
|
||
expect(conf.baseConfig).toMatchInlineSnapshot(` | ||
Object { | ||
"extends": "<PROJECT_ROOT>/packages/gatsby/src/utils/eslint/required.js", | ||
} | ||
`) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Rule } from "eslint" | ||
import { GatsbyReduxStore } from "../redux" | ||
|
||
export function isPageTemplate( | ||
s: GatsbyReduxStore, | ||
c: Rule.RuleContext | ||
): boolean { | ||
const filename = c.getFilename() | ||
if (!filename) { | ||
return false | ||
} | ||
return s.getState().components.has(filename) | ||
} | ||
|
||
export function test(t): any { | ||
return Object.assign(t, { | ||
parserOptions: { | ||
sourceType: `module`, | ||
ecmaVersion: 9, | ||
}, | ||
}) | ||
} |
Oops, something went wrong.