refactor, more tests (#444)

* async/await refactor, more testing

* add funding docs

* test fix

* dependency updates
This commit is contained in:
Matthias Kretschmann 2024-04-08 12:09:48 +01:00 committed by GitHub
parent 83e2ae1daa
commit a036be8087
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 1624 additions and 1479 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
github: kremalicious
patreon: kremalicious
custom: ['https://kremalicious.com/thanks']

View File

@ -24,6 +24,7 @@ By combining this plugin with [gatsby-plugin-meta-redirect](https://github.com/g
- [Documentation](#documentation) - [Documentation](#documentation)
- [Plugin Development](#plugin-development) - [Plugin Development](#plugin-development)
- [Changelog](#changelog) - [Changelog](#changelog)
- [Sponsorship](#sponsorship)
- [License](#license) - [License](#license)
--- ---
@ -92,6 +93,12 @@ npm run release
See [CHANGELOG.md](CHANGELOG.md). See [CHANGELOG.md](CHANGELOG.md).
## Sponsorship
[![](https://img.shields.io/static/v1?label=Say%20Thanks%20With%20Web3&labelColor=%2343a699&message=%E2%9D%A4&logo=Ethereum&color=%23fe8e86&style=for-the-badge)](https://kremalicious.com/thanks)
[![](https://img.shields.io/static/v1?label=Say%20Thanks%20With%20GitHub&labelColor=%2343a699&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86&style=for-the-badge)](https://github.com/sponsors/kremalicious)
## License ## License
The MIT License The MIT License
@ -107,9 +114,3 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
--- ---
Made with ♥ by [Matthias Kretschmann](https://matthiaskretschmann.com) ([@kremalicious](https://github.com/kremalicious)) Made with ♥ by [Matthias Kretschmann](https://matthiaskretschmann.com) ([@kremalicious](https://github.com/kremalicious))
Say thanks with BTC:
`35UUssHexVK48jbiSgTxa4QihEoCqrwCTG`
Say thanks with ETH:
`0x04354F554536DA108726829207958d3E277f0210`

2847
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,18 +20,17 @@
"maintained node versions" "maintained node versions"
], ],
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.23.0", "@babel/cli": "^7.24.1",
"@babel/core": "^7.23.0", "@babel/core": "^7.24.4",
"@babel/preset-env": "^7.22.20", "@babel/preset-env": "^7.24.4",
"auto-changelog": "^2.4.0", "auto-changelog": "^2.4.0",
"chalk": "^5.3.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.50.0", "eslint": "^8.50.0",
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.1.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"prettier": "^3.0.3", "prettier": "^3.2.5",
"release-it": "^16.2.1" "release-it": "^17.1.1"
}, },
"peerDependencies": { "peerDependencies": {
"gatsby": "^4.0.0 || ^5.0.0", "gatsby": "^4.0.0 || ^5.0.0",

View File

@ -1,68 +1,100 @@
import chalk from 'chalk' exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
export function createPages({ graphql, actions }, pluginOptions) { // Define the type definitions for the `MarkdownRemark` node
const typeDefs = `
type MarkdownRemark implements Node {
fields: MarkdownRemarkFields
frontmatter: MarkdownRemarkFrontmatter
}
type MarkdownRemarkFields {
slug: String
}
type MarkdownRemarkFrontmatter {
slug: String
}
`
// Create the type definitions
createTypes(typeDefs)
}
export async function createPages({ graphql, actions }, pluginOptions) {
const { createRedirect } = actions const { createRedirect } = actions
const markdownQuery = pluginOptions.query || 'allMarkdownRemark' const markdownQuery = pluginOptions.query || 'allMarkdownRemark'
return new Promise((resolve, reject) => { try {
resolve( const { data } = await graphql(`
graphql( {
` q: ${markdownQuery}(filter: { frontmatter: { redirect_from: { ne: null } } }) {
{ edges {
q: ${markdownQuery}( node {
filter: { frontmatter: { redirect_from: { ne: null } } } fields {
) { slug
edges { }
node { frontmatter {
fields { slug
slug redirect_from
}
frontmatter {
redirect_from
}
}
} }
} }
} }
`
).then((result) => {
if (result.errors) {
console.log(result.errors) // eslint-disable-line no-console
reject(result.errors)
return
} }
}
`)
const allPosts = result.data.q.edges const allPosts = data?.q?.edges
const redirects = [] if (!allPosts || allPosts.length === 0) {
console.log(
'%c %s %c %s',
'color:orange',
'warning',
'color:none',
'no posts with redirect_from found'
)
return
}
// For all posts with redirect_from frontmatter, const redirects = []
// extract all values and push to redirects array
allPosts.forEach((post) => {
redirects.push({
from: post.node.frontmatter.redirect_from,
to: post.node.fields.slug
})
})
// Create redirects from the just constructed array allPosts.forEach(({ node }) => {
redirects.forEach(({ from, to }) => { let _slug
// iterate through all `from` array items const { redirect_from, slug } = node.frontmatter
from.forEach((from) => { if (!slug) _slug = node.fields?.slug
createRedirect({ if (!_slug) {
fromPath: from, console.log(
toPath: to, '%c %s %c %s',
isPermanent: true, 'color:orange',
redirectInBrowser: true 'warning',
}) 'color:none',
}) 'no slug found in frontmatter or fields'
})
resolve(
console.log(`${chalk.green('success')} create redirects`) // eslint-disable-line no-console
) )
return
}
redirect_from.forEach((from) => {
redirects.push({ from, to: _slug })
}) })
})
redirects.forEach(({ from, to }) => {
createRedirect({
fromPath: from,
toPath: to,
isPermanent: true,
redirectInBrowser: true
})
})
console.log(
'%c %s %c %s',
'color:green',
'success',
'color:none',
`created ${redirects.length} redirects`
) )
}) } catch (error) {
console.error(error.message)
}
} }

View File

@ -14,6 +14,22 @@ describe('createPages', () => {
let actions let actions
let consoleLogSpy let consoleLogSpy
const markdownQuery = 'allMarkdownRemark'
const edges = [
{
node: {
fields: { slug: '/post-1' },
frontmatter: { redirect_from: ['/old-url-1', '/old-url-2'] }
}
},
{
node: {
fields: { slug: '/post-2' },
frontmatter: { redirect_from: ['/old-url-3'] }
}
}
]
beforeEach(() => { beforeEach(() => {
actions = { createRedirect: jest.fn() } actions = { createRedirect: jest.fn() }
pluginOptions = { query: 'allMarkdownRemark' } pluginOptions = { query: 'allMarkdownRemark' }
@ -25,39 +41,34 @@ describe('createPages', () => {
consoleLogSpy.mockRestore() consoleLogSpy.mockRestore()
}) })
it('should create redirects correctly', async () => { it('should create redirects for each post with a redirect_from field', async () => {
graphql.mockReturnValueOnce( const data = { q: { edges } }
Promise.resolve({ graphql.mockReturnValueOnce(Promise.resolve({ data }))
data: { const createRedirectSpy = jest.spyOn(actions, 'createRedirect')
q: {
edges: [
{
node: {
fields: { slug: '/post-1/' },
frontmatter: { redirect_from: ['/old-url-1', '/old-url-2'] }
}
}
]
}
}
})
)
await createPages({ graphql, actions }, pluginOptions) await createPages({ graphql, actions }, { query: markdownQuery })
expect(actions.createRedirect).toHaveBeenCalledTimes(2) expect(createRedirectSpy).toHaveBeenCalledTimes(3)
expect(actions.createRedirect).toHaveBeenCalledWith({ expect(createRedirectSpy).toHaveBeenCalledWith({
fromPath: '/old-url-1', fromPath: '/old-url-1',
toPath: '/post-1/', toPath: '/post-1',
isPermanent: true, isPermanent: true,
redirectInBrowser: true redirectInBrowser: true
}) })
expect(actions.createRedirect).toHaveBeenCalledWith({ expect(createRedirectSpy).toHaveBeenCalledWith({
fromPath: '/old-url-2', fromPath: '/old-url-2',
toPath: '/post-1/', toPath: '/post-1',
isPermanent: true, isPermanent: true,
redirectInBrowser: true redirectInBrowser: true
}) })
expect(createRedirectSpy).toHaveBeenCalledWith({
fromPath: '/old-url-3',
toPath: '/post-2',
isPermanent: true,
redirectInBrowser: true
})
createRedirectSpy.mockRestore()
}) })
it('should use pluginOptions.query when defined', async () => { it('should use pluginOptions.query when defined', async () => {
@ -83,16 +94,37 @@ describe('createPages', () => {
await createPages({ graphql, actions }, pluginOptions) await createPages({ graphql, actions }, pluginOptions)
}) })
it('should log and reject errors when the GraphQL query fails', async () => { it('should log a success message when redirects are created', async () => {
graphql.mockReturnValueOnce( const data = { q: { edges } }
Promise.resolve({ errors: [{ message: 'GraphQL error' }] }) graphql.mockReturnValueOnce(Promise.resolve({ data }))
const createRedirectSpy = jest.spyOn(actions, 'createRedirect')
await createPages({ graphql, actions }, { query: markdownQuery })
expect(createRedirectSpy).toHaveBeenCalledTimes(3)
expect(console.log).toHaveBeenCalledWith(
'%c %s %c %s',
'color:green',
'success',
'color:none',
'created 3 redirects'
) )
})
it('should log an error message when an error occurs', async () => {
const error = new Error('GraphQL query failed')
graphql.mockImplementationOnce(() => {
throw error
})
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
try { try {
await createPages({ graphql, actions }, pluginOptions) await createPages({ graphql, actions }, pluginOptions)
} catch (error) { } catch (error) {
expect(console.log).toHaveBeenCalledWith([{ message: 'GraphQL error' }]) expect(console.error).toHaveBeenCalledWith('GraphQL query failed')
expect(error).toEqual([{ message: 'GraphQL error' }])
} }
consoleSpy.mockRestore()
}) })
}) })

View File

@ -1,5 +1,4 @@
{ {
"rootDir": "../", "rootDir": "../",
"coverageDirectory": "<rootDir>/coverage/", "coverageDirectory": "<rootDir>/coverage/"
"transformIgnorePatterns": ["node_modules/(?!chalk/.*)"]
} }