Compare commits
31 Commits
@matthiese
...
main
Author | SHA1 | Date |
---|---|---|
Adam Matthiesen | dfaa820f06 | |
github-actions[bot] | b98c62f0b6 | |
dependabot[bot] | 6ccd6dc5b8 | |
dependabot[bot] | 3f6e9a996b | |
github-actions[bot] | a1ea3d7a78 | |
create-issue-branch[bot] | 6464de28f8 | |
github-actions[bot] | b375a29495 | |
create-issue-branch[bot] | e54bfbbfd2 | |
Rishi Raj Jain | cf327f266f | |
github-actions[bot] | fae567c480 | |
Adam Matthiesen | 784cb63fcb | |
github-actions[bot] | b7eec6bb98 | |
create-issue-branch[bot] | 337a5b09ce | |
github-actions[bot] | 32e2569373 | |
create-issue-branch[bot] | fc94343873 | |
Adam Matthiesen | 8fbe34124d | |
github-actions[bot] | cb033a0bd2 | |
Adam Matthiesen | 4df3b17fea | |
Adam Matthiesen | e1dbf1dc5c | |
create-issue-branch[bot] | 773376207b | |
Adam Matthiesen | 4b159a9bbb | |
Adam Matthiesen | b634d82bc1 | |
Adam Matthiesen | bcf5a896a2 | |
Adam Matthiesen | ce1401ba81 | |
Adam Matthiesen | 17d2c9f272 | |
Adam Matthiesen | 943819d43b | |
Adam Matthiesen | 453880f1e3 | |
github-actions[bot] | 51060f8a27 | |
Adam Matthiesen | d1b675757c | |
create-issue-branch[bot] | d86777fdae | |
Adam Matthiesen | 1fa679f11a |
|
@ -1,8 +1,8 @@
|
||||||
name: "Changesets: Build Changesets for Dependabot"
|
name: "Changesets: Build Changesets for Dependabot"
|
||||||
|
|
||||||
#on: pull_request
|
on: pull_request
|
||||||
on:
|
#on:
|
||||||
workflow_dispatch:
|
#workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
name: "Changesets: Publish Packages from main"
|
name: "Changesets: Publish Packages from main"
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
@ -14,4 +14,4 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: kevinzunigacuellar/coauthor-action@v0.1.1
|
- uses: kevinzunigacuellar/coauthor-action@v0.1.2
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
name: "Sync: GitHub => GitLab"
|
name: "Sync to GitLab"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
- push
|
- push
|
||||||
- delete
|
- delete
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync:
|
sync:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -12,7 +14,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: jauderho/git-repo-sync@63782025e80e84c48b25a1ee6bb9a22a3bd570d3
|
- uses: MatthiesenXYZ/git-sync-action@v1.1
|
||||||
with:
|
with:
|
||||||
# Such as https://github.com/wangchucheng/git-repo-sync.git
|
# Such as https://github.com/wangchucheng/git-repo-sync.git
|
||||||
target-url: ${{ secrets.GITLAB_URL }}
|
target-url: ${{ secrets.GITLAB_URL }}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
An Integration to bring your Hashnode Headless Blog content into Astro!
|
An Integration to bring your Hashnode Headless Blog content into Astro!
|
||||||
|
|
||||||
|
View a live demo [Here](https://astro-hashnode-playground.vercel.app/)
|
||||||
|
|
||||||
To see how to get started, check out the [package README](./package/README.md)
|
To see how to get started, check out the [package README](./package/README.md)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
@ -29,4 +31,6 @@ You can now edit files in `package`. Please note that making changes to those fi
|
||||||
|
|
||||||
[MIT Licensed](./LICENSE). Made with ❤️ by [Adam M.](https://github.com/adammatthiesen).
|
[MIT Licensed](./LICENSE). Made with ❤️ by [Adam M.](https://github.com/adammatthiesen).
|
||||||
|
|
||||||
|
This project is cloned to [Gitea](https://git.matthiesen.dev)
|
||||||
|
|
||||||
This project is also cloned to [GitLab](https://gitlab.com/matthiesenxyz/astro-hashnode)
|
This project is also cloned to [GitLab](https://gitlab.com/matthiesenxyz/astro-hashnode)
|
|
@ -13,7 +13,7 @@
|
||||||
"ci:publish": "pnpm changeset publish"
|
"ci:publish": "pnpm changeset publish"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.6.0",
|
"@biomejs/biome": "1.6.1",
|
||||||
"@changesets/cli": "^2.27.1"
|
"@changesets/cli": "^2.27.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,60 @@
|
||||||
# @matthiesenxyz/astro-hashnode
|
# @matthiesenxyz/astro-hashnode
|
||||||
|
|
||||||
|
## 0.1.8
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 6ccd6dc: Bump dependencies:
|
||||||
|
|
||||||
|
- @tailwindcss/vite from to
|
||||||
|
- astro-font from to
|
||||||
|
- tailwindcss from to
|
||||||
|
- @astrojs/node from to
|
||||||
|
- astro from to
|
||||||
|
|
||||||
|
## 0.1.7
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 6464de2: [fix] add extra fallback option for ogImage
|
||||||
|
|
||||||
|
## 0.1.6
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- cf327f2: Update README.md
|
||||||
|
- e54bfbb: [Internal] better handling of the `hashnodeURL` input to verify that including `http` or `https` in the URL does not break the entire integration
|
||||||
|
|
||||||
|
## 0.1.5
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 784cb63: view transition updates
|
||||||
|
|
||||||
|
## 0.1.4
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 337a5b0: added new keyword "cms" and updated Dependencies
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- fc94343: This updates internal `AIK` as well as impliments ViewTransitions with a disable switch in the user config
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 7733762: Add SSR support for blog posts (Tags are still prerendered)
|
||||||
|
|
||||||
|
## 0.1.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- d86777f: adds exports for HashnodeAPI, as well as components so users can import the pre-built components for their own layout
|
||||||
|
|
||||||
## 0.1.0
|
## 0.1.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|
|
@ -11,7 +11,7 @@ pnpm astro add @matthiesenxyz/astro-hashnode
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm astro add @matthiesenxyz/astro-hashnode
|
npx astro add @matthiesenxyz/astro-hashnode
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -54,6 +54,7 @@ export default defineConfig({
|
||||||
astroHashnode({
|
astroHashnode({
|
||||||
hashnodeURL: 'astroplayground.hashnode.dev', // Your hashnode URL
|
hashnodeURL: 'astroplayground.hashnode.dev', // Your hashnode URL
|
||||||
landingPage: true, // Lets you disable the default landing page!
|
landingPage: true, // Lets you disable the default landing page!
|
||||||
|
useViewTransitions: true, // Lets you enable/disable the default included ViewTransitions.
|
||||||
layoutComponent: './src/layouts/YourLayout.astro' // Lets you change the default Layout.astro being used by the Integration Pages.
|
layoutComponent: './src/layouts/YourLayout.astro' // Lets you change the default Layout.astro being used by the Integration Pages.
|
||||||
verbose: false // Change to Verbose console output
|
verbose: false // Change to Verbose console output
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@matthiesenxyz/astro-hashnode",
|
"name": "@matthiesenxyz/astro-hashnode",
|
||||||
"version": "0.1.0",
|
"version": "0.1.8",
|
||||||
"description": "An Integration to bring your Hashnode Headless Blog content into Astro!",
|
"description": "An Integration to bring your Hashnode Headless Blog content into Astro!",
|
||||||
"author": {
|
"author": {
|
||||||
"email": "adam@matthiesen.xyz",
|
"email": "adam@matthiesen.xyz",
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
"astro-integration",
|
"astro-integration",
|
||||||
"withastro",
|
"withastro",
|
||||||
"astro",
|
"astro",
|
||||||
|
"cms",
|
||||||
"hashnode",
|
"hashnode",
|
||||||
"blog",
|
"blog",
|
||||||
"graphql",
|
"graphql",
|
||||||
|
@ -30,7 +31,9 @@
|
||||||
"src"
|
"src"
|
||||||
],
|
],
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts",
|
||||||
|
"./api": "./src/hn-gql/index.ts",
|
||||||
|
"./components/*": "./src/components/*"
|
||||||
},
|
},
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
@ -41,15 +44,15 @@
|
||||||
"vite": "^5.1.5"
|
"vite": "^5.1.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "4.0.0-alpha.7",
|
"@tailwindcss/vite": "4.0.0-alpha.9",
|
||||||
"astro-font": "0.0.77",
|
"astro-font": "^0.0.78",
|
||||||
"astro-integration-kit": "^0.5.1",
|
"astro-integration-kit": "0.6.0",
|
||||||
"astro-remote": "^0.3.2",
|
"astro-remote": "^0.3.2",
|
||||||
"astro-seo": "^0.8.3",
|
"astro-seo": "^0.8.3",
|
||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"graphql-request": "^6.1.0",
|
"graphql-request": "^6.1.0",
|
||||||
"picocolors": "1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"tailwindcss": "4.0.0-alpha.7",
|
"tailwindcss": "4.0.0-alpha.9",
|
||||||
"ultrahtml": "^1.5.3"
|
"ultrahtml": "^1.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,7 @@ export default defineIntegration({
|
||||||
optionsSchema,
|
optionsSchema,
|
||||||
plugins: [ ...corePlugins, addDtsPlugin ],
|
plugins: [ ...corePlugins, addDtsPlugin ],
|
||||||
setup({ options }) {
|
setup({ options }) {
|
||||||
|
type outputType = "static" | "hybrid" | "server";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"astro:config:setup": ({
|
"astro:config:setup": ({
|
||||||
|
@ -50,6 +51,13 @@ export default defineIntegration({
|
||||||
throw new AstroError(message)
|
throw new AstroError(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if output is static or hybrid
|
||||||
|
const checkIfStatic = (output: outputType) => {
|
||||||
|
if (output === "static") { return true }
|
||||||
|
if (output === "hybrid") { return true }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
hashLogNoVerbose("Setting up Astro-Hashnode Integration")
|
hashLogNoVerbose("Setting up Astro-Hashnode Integration")
|
||||||
|
|
||||||
// Check for Hashnode URL
|
// Check for Hashnode URL
|
||||||
|
@ -61,8 +69,7 @@ export default defineIntegration({
|
||||||
|
|
||||||
hashLog("Setting up Virtual Imports and Layout Component")
|
hashLog("Setting up Virtual Imports and Layout Component")
|
||||||
// Setup Layout Component
|
// Setup Layout Component
|
||||||
// biome-ignore lint/suspicious/noImplicitAnyLet: This is a false positive
|
let layoutComponentPath: string;
|
||||||
let layoutComponentPath
|
|
||||||
|
|
||||||
if (options.layoutComponent) {
|
if (options.layoutComponent) {
|
||||||
layoutComponentPath = rootResolve(options.layoutComponent)
|
layoutComponentPath = rootResolve(options.layoutComponent)
|
||||||
|
@ -81,8 +88,8 @@ export default defineIntegration({
|
||||||
content: readFileSync(resolve("./definitions/astro-hashnode.d.ts"), "utf-8"),
|
content: readFileSync(resolve("./definitions/astro-hashnode.d.ts"), "utf-8"),
|
||||||
})
|
})
|
||||||
|
|
||||||
hashLog("Setting up 'Tailwind CSS v4' Integration")
|
|
||||||
// Add & Setup Tailwind CSS
|
// Add & Setup Tailwind CSS
|
||||||
|
hashLog("Setting up 'Tailwind CSS v4' Integration")
|
||||||
const twplugin = tailwindcss();
|
const twplugin = tailwindcss();
|
||||||
for (const twp of twplugin) {
|
for (const twp of twplugin) {
|
||||||
addVitePlugin(twp);
|
addVitePlugin(twp);
|
||||||
|
@ -102,22 +109,19 @@ export default defineIntegration({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
hashLog("Setting up Page Routes")
|
|
||||||
// Add Page Routes
|
// Add Page Routes
|
||||||
|
hashLog("Setting up Page Routes")
|
||||||
if (options.landingPage) {
|
if (options.landingPage) {
|
||||||
injectRoute({
|
injectRoute({
|
||||||
pattern: config.base,
|
pattern: config.base,
|
||||||
entrypoint: resolve("./pages/index.astro"),
|
entrypoint: resolve("./pages/index.astro"),
|
||||||
|
prerender: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
injectRoute({
|
injectRoute({
|
||||||
pattern: `${config.base}blog`,
|
pattern: `${config.base}blog`,
|
||||||
entrypoint: resolve("./pages/blog/index.astro"),
|
entrypoint: resolve("./pages/blog/index.astro"),
|
||||||
})
|
})
|
||||||
injectRoute({
|
|
||||||
pattern: `${config.base}blog/[slug]`,
|
|
||||||
entrypoint: resolve("./pages/blog/[slug].astro"),
|
|
||||||
})
|
|
||||||
injectRoute({
|
injectRoute({
|
||||||
pattern: `${config.base}blog/about`,
|
pattern: `${config.base}blog/about`,
|
||||||
entrypoint: resolve("./pages/blog/about.astro"),
|
entrypoint: resolve("./pages/blog/about.astro"),
|
||||||
|
@ -125,7 +129,26 @@ export default defineIntegration({
|
||||||
injectRoute({
|
injectRoute({
|
||||||
pattern: `${config.base}blog/tags/[tag]`,
|
pattern: `${config.base}blog/tags/[tag]`,
|
||||||
entrypoint: resolve("./pages/blog/tags/[tag].astro"),
|
entrypoint: resolve("./pages/blog/tags/[tag].astro"),
|
||||||
|
prerender: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add Blog Post Routes based on output type
|
||||||
|
if ( checkIfStatic(config.output) ) {
|
||||||
|
// Static & Hybrid Routes
|
||||||
|
injectRoute({
|
||||||
|
pattern: `${config.base}blog/[slug]`,
|
||||||
|
entrypoint: resolve("./pages/blog/[slug].astro"),
|
||||||
|
prerender: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Server Routes
|
||||||
|
injectRoute({
|
||||||
|
pattern: `${config.base}blog/[slug]`,
|
||||||
|
entrypoint: resolve("./pages/ssr-pages/[slug].astro"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"astro:config:done": ({ logger }) => {
|
"astro:config:done": ({ logger }) => {
|
||||||
const HashLogger = logger.fork(c.bold(c.blue("Astro-Hashnode")));
|
const HashLogger = logger.fork(c.bold(c.blue("Astro-Hashnode")));
|
||||||
|
|
|
@ -20,16 +20,24 @@ const { post } = Astro.props;
|
||||||
height={50}
|
height={50}
|
||||||
class="rounded-3xl mr-3"
|
class="rounded-3xl mr-3"
|
||||||
loading="eager"
|
loading="eager"
|
||||||
|
transition:animate={"fade"}
|
||||||
|
transition:name={'author:' + post.author.profilePicture}
|
||||||
/>
|
/>
|
||||||
<div class="mt-3 flex">
|
<div class="mt-3 flex">
|
||||||
<span>{post.author.name}</span>
|
<span transition:animate={"fade"}
|
||||||
|
transition:name={'author:' + post.author.name}
|
||||||
|
>{post.author.name}</span>
|
||||||
<span class="mx-3 block font-bold text-slate-500">.</span>
|
<span class="mx-3 block font-bold text-slate-500">.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-5 flex w-full flex-row items-center justify-center md:mb-0 md:w-auto md:justify-start">
|
<div class="mb-5 flex w-full flex-row items-center justify-center md:mb-0 md:w-auto md:justify-start" >
|
||||||
<span>{getFormattedDate(post.publishedAt)}</span>
|
<span transition:animate={"fade"}
|
||||||
|
transition:name={'hero:' + post.publishedAt}
|
||||||
|
>{getFormattedDate(post.publishedAt)}</span>
|
||||||
<span class="mx-3 block font-bold text-slate-500">.</span>
|
<span class="mx-3 block font-bold text-slate-500">.</span>
|
||||||
<span>{post.readTimeInMinutes} min read</span>
|
<span transition:animate={"fade"}
|
||||||
|
transition:name={'hero:' + post.readTimeInMinutes}
|
||||||
|
>{post.readTimeInMinutes} min read</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -7,6 +7,27 @@ const aboutPageData = await getAboutPage();
|
||||||
|
|
||||||
const baseURL = import.meta.env.BASE_URL;
|
const baseURL = import.meta.env.BASE_URL;
|
||||||
|
|
||||||
|
const pathname = new URL(Astro.request.url).pathname;
|
||||||
|
|
||||||
|
// remove leading and trailing slash
|
||||||
|
const removeTrailingAndLeadingSlash = (str:string) => {
|
||||||
|
// define checked string as the original string
|
||||||
|
let checkedStr = str;
|
||||||
|
|
||||||
|
// remove leading slash
|
||||||
|
if (str.startsWith("/")) {
|
||||||
|
checkedStr = str.slice(1);
|
||||||
|
}
|
||||||
|
// remove trailing slash
|
||||||
|
if (str.endsWith("/")) {
|
||||||
|
checkedStr = str.slice(0, -1);
|
||||||
|
}
|
||||||
|
// return checked string
|
||||||
|
return checkedStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPath = removeTrailingAndLeadingSlash(pathname);
|
||||||
|
|
||||||
---
|
---
|
||||||
<header class="flex bg-blue-200 w-full p-3">
|
<header class="flex bg-blue-200 w-full p-3">
|
||||||
<h1 class="text-2xl">
|
<h1 class="text-2xl">
|
||||||
|
@ -35,8 +56,22 @@ const baseURL = import.meta.env.BASE_URL;
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="ml-5 pt-0.5 text-lg">
|
<div class="ml-5 pt-0.5 text-lg">
|
||||||
<a class="mr-3" href={baseURL}>Home</a>
|
<a
|
||||||
<a class="mr-3" href={`${baseURL}blog`}>Blog</a>
|
class=`mr-3 ${currentPath === "" ? "font-bold" : ""}`
|
||||||
{aboutPageData && <a href={`${baseURL}blog/about/`}>About</a>}
|
href={baseURL}
|
||||||
|
>Home
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class=`mr-3 ${currentPath === "blog" ? "font-bold" : ""}`
|
||||||
|
href={`${baseURL}blog`}
|
||||||
|
>Blog
|
||||||
|
</a>
|
||||||
|
{ aboutPageData && (
|
||||||
|
<a
|
||||||
|
class=`mr-3 ${currentPath === "blog/about" ? "font-bold" : ""}`
|
||||||
|
href={`${baseURL}blog/about`}
|
||||||
|
>About
|
||||||
|
</a>
|
||||||
|
) }
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -26,6 +26,7 @@ const p = await getPost(post.slug);
|
||||||
src={p.coverImage.url}
|
src={p.coverImage.url}
|
||||||
alt={post.title}
|
alt={post.title}
|
||||||
inferSize={true}
|
inferSize={true}
|
||||||
|
transition:name={'hero:' + p.coverImage.url}
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col m-4">
|
<div class="flex flex-col m-4">
|
||||||
<p class="mb-2 text-lg">{post.brief}</p>
|
<p class="mb-2 text-lg">{post.brief}</p>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
declare module 'virtual:astro-hashnode/config' {
|
declare module 'virtual:astro-hashnode/config' {
|
||||||
const userConfig: import("./src/schemas/user-config").Options;
|
const Config: import("./src/schemas/user-config").Options;
|
||||||
export default config as userConfig;
|
export default config as Config;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'virtual:astro-hashnode/components' {
|
declare module 'virtual:astro-hashnode/components' {
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
import { gql, GraphQLClient } from "graphql-request";
|
import { gql, GraphQLClient } from "graphql-request";
|
||||||
import type { AllPostsData, PostOrPageData, PublicationData } from "./schema";
|
import type { AllPostsData, PostOrPageData, PublicationData } from "./schema";
|
||||||
import Config from "virtual:astro-hashnode/config";
|
import config from "virtual:astro-hashnode/config";
|
||||||
|
|
||||||
export const getClient = () => {
|
export const getClient = () => {
|
||||||
return new GraphQLClient("https://gql.hashnode.com")
|
return new GraphQLClient("https://gql.hashnode.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function removeHTTPSProtocol(url: string) {
|
||||||
|
return url.replace(/^https?:\/\//, '');
|
||||||
|
}
|
||||||
|
export function removeHTTPProtocol(url: string) {
|
||||||
|
const fixHTTPS = removeHTTPSProtocol(url);
|
||||||
|
return fixHTTPS.replace(/^http?:\/\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newURL = removeHTTPProtocol(config.hashnodeURL);
|
||||||
|
|
||||||
export const getAllPosts = async () => {
|
export const getAllPosts = async () => {
|
||||||
const client = getClient();
|
const client = getClient();
|
||||||
|
|
||||||
const allPosts = await client.request<AllPostsData>(
|
const allPosts = await client.request<AllPostsData>(
|
||||||
gql`
|
gql`
|
||||||
query allPosts {
|
query allPosts {
|
||||||
publication(host: "${Config.hashnodeURL}") {
|
publication(host: "${newURL}") {
|
||||||
title
|
title
|
||||||
posts(first: 20) {
|
posts(first: 20) {
|
||||||
pageInfo{
|
pageInfo{
|
||||||
|
@ -56,7 +66,7 @@ export const getPost = async (slug: string) => {
|
||||||
const data = await client.request<PostOrPageData>(
|
const data = await client.request<PostOrPageData>(
|
||||||
gql`
|
gql`
|
||||||
query postDetails($slug: String!) {
|
query postDetails($slug: String!) {
|
||||||
publication(host: "${Config.hashnodeURL}") {
|
publication(host: "${newURL}") {
|
||||||
post(slug: $slug) {
|
post(slug: $slug) {
|
||||||
author{
|
author{
|
||||||
name
|
name
|
||||||
|
@ -92,7 +102,7 @@ export const getAboutPage = async () => {
|
||||||
const page = await client.request<PostOrPageData>(
|
const page = await client.request<PostOrPageData>(
|
||||||
gql`
|
gql`
|
||||||
query pageData {
|
query pageData {
|
||||||
publication(host: "${Config.hashnodeURL}") {
|
publication(host: "${newURL}") {
|
||||||
staticPage(slug: "about") {
|
staticPage(slug: "about") {
|
||||||
title
|
title
|
||||||
content {
|
content {
|
||||||
|
@ -114,11 +124,14 @@ export const getPublication = async () => {
|
||||||
const data = await client.request<PublicationData>(
|
const data = await client.request<PublicationData>(
|
||||||
gql`
|
gql`
|
||||||
query pubData {
|
query pubData {
|
||||||
publication(host: "${Config.hashnodeURL}") {
|
publication(host: "${newURL}") {
|
||||||
title
|
title
|
||||||
displayTitle
|
displayTitle
|
||||||
descriptionSEO
|
descriptionSEO
|
||||||
favicon
|
favicon
|
||||||
|
author {
|
||||||
|
profilePicture
|
||||||
|
}
|
||||||
preferences {
|
preferences {
|
||||||
logo
|
logo
|
||||||
disableFooterBranding
|
disableFooterBranding
|
||||||
|
|
|
@ -60,6 +60,9 @@ export const PublicationDataSchema = z.object({
|
||||||
displayTitle: z.string(),
|
displayTitle: z.string(),
|
||||||
descriptionSEO: z.string(),
|
descriptionSEO: z.string(),
|
||||||
favicon: z.string(),
|
favicon: z.string(),
|
||||||
|
author: z.object({
|
||||||
|
profilePicture: z.string(),
|
||||||
|
}),
|
||||||
preferences: z.object({
|
preferences: z.object({
|
||||||
logo: z.string(),
|
logo: z.string(),
|
||||||
disableFooterBranding: z.boolean(),
|
disableFooterBranding: z.boolean(),
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import astroHashnode from "./astro-hashnode.js";
|
import astroHashnode from "./astro-hashnode";
|
||||||
|
|
||||||
export default astroHashnode;
|
export default astroHashnode;
|
|
@ -6,6 +6,10 @@ import { getPublication } from '../hn-gql'
|
||||||
import { SEO } from "astro-seo";
|
import { SEO } from "astro-seo";
|
||||||
import { AstroFont } from "astro-font";
|
import { AstroFont } from "astro-font";
|
||||||
import type { AstroHashnodeLayoutProps } from '../proptypes/layouttypes'
|
import type { AstroHashnodeLayoutProps } from '../proptypes/layouttypes'
|
||||||
|
import { ViewTransitions } from 'astro:transitions';
|
||||||
|
import config from "virtual:astro-hashnode/config";
|
||||||
|
|
||||||
|
const useTranstions = config.useViewTransitions;
|
||||||
|
|
||||||
const pubData = await getPublication();
|
const pubData = await getPublication();
|
||||||
|
|
||||||
|
@ -37,7 +41,7 @@ const { pageTitle, hideFooter, hideHeader, ogImage } = Astro.props as AstroHashn
|
||||||
basic: {
|
basic: {
|
||||||
title: pageTitle ? pageTitle + " | " + pubHeader : pubHeader,
|
title: pageTitle ? pageTitle + " | " + pubHeader : pubHeader,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
image: ogImage || pubData.favicon,
|
image: ogImage || pubData.favicon || pubData.author.profilePicture,
|
||||||
},
|
},
|
||||||
optional: {
|
optional: {
|
||||||
description: pubData.descriptionSEO,
|
description: pubData.descriptionSEO,
|
||||||
|
@ -54,10 +58,11 @@ const { pageTitle, hideFooter, hideHeader, ogImage } = Astro.props as AstroHashn
|
||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{useTranstions && <ViewTransitions />}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
{!hideHeader && <Header />}
|
{!hideHeader && <Header transition:animate="none" />}
|
||||||
<div class="flex flex-wrap flex-col mt-0 mr-auto mb-0 ml-auto lg:w-[60%]">
|
<div class="flex flex-wrap flex-col mt-0 mr-auto mb-0 ml-auto lg:w-[60%]">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,7 @@ export async function getStaticPaths() {
|
||||||
}
|
}
|
||||||
const { slug } = Astro.params;
|
const { slug } = Astro.params;
|
||||||
const post = await getPost(slug);
|
const post = await getPost(slug);
|
||||||
|
|
||||||
---
|
---
|
||||||
<Layout pageTitle={post.title} ogImage={post.coverImage.url}>
|
<Layout pageTitle={post.title} ogImage={post.coverImage.url}>
|
||||||
<article class="bg-white p-3 mt-3 flex flex-col">
|
<article class="bg-white p-3 mt-3 flex flex-col">
|
||||||
|
@ -27,11 +28,12 @@ const post = await getPost(slug);
|
||||||
alt={post.title}
|
alt={post.title}
|
||||||
inferSize={true}
|
inferSize={true}
|
||||||
loading="eager"
|
loading="eager"
|
||||||
|
transition:name={'hero:' + post.coverImage.url}
|
||||||
/>
|
/>
|
||||||
<h1 class="text-4xl font-bold pt-5">{post.title}</h1>
|
<h1 class="text-4xl font-bold pt-5" transition:name={'banner'} transition:animate={'fade'}>{post.title}</h1>
|
||||||
<h2 class="text-xl pt-3 pb-3" aria-label="CoverPhoto Subtitle">{post.subtitle}</h2>
|
<h2 class="text-xl pt-3 pb-3" aria-label="CoverPhoto Subtitle">{post.subtitle}</h2>
|
||||||
|
|
||||||
<Author post={post} />
|
<Author post={post}/>
|
||||||
|
|
||||||
<div class="flex flex-wrap justify-center items-center mt-5 mb-5">
|
<div class="flex flex-wrap justify-center items-center mt-5 mb-5">
|
||||||
{post.tags && post.tags.map((tag) => <Tag tag={tag} />)}
|
{post.tags && post.tags.map((tag) => <Tag tag={tag} />)}
|
||||||
|
|
|
@ -9,7 +9,9 @@ const data = await getAboutPage();
|
||||||
|
|
||||||
<Layout pageTitle="About">
|
<Layout pageTitle="About">
|
||||||
<div class="flex flex-col justify-center p-2">
|
<div class="flex flex-col justify-center p-2">
|
||||||
<h2 class="text-3xl mb-3">{data.title} Page</h2>
|
<h2 class="text-3xl mb-3"
|
||||||
|
transition:name={'banner'}
|
||||||
|
transition:animate={'fade'}>{data.title}</h2>
|
||||||
<div class="about-content">
|
<div class="about-content">
|
||||||
<Markdown content={data.content.markdown}
|
<Markdown content={data.content.markdown}
|
||||||
components={{
|
components={{
|
||||||
|
|
|
@ -10,7 +10,7 @@ const allPosts = data.publication.posts.edges;
|
||||||
---
|
---
|
||||||
<Layout pageTitle="Blog">
|
<Layout pageTitle="Blog">
|
||||||
<div class="flex flex-col justify-center items-center p-2">
|
<div class="flex flex-col justify-center items-center p-2">
|
||||||
<h2 class="text-2xl pt-2 font-semibold">{`Welcome to ${pub.displayTitle || pub.title}`}</h2>
|
<h2 transition:animate={'fade'} transition:name={'banner'} class="text-2xl pt-2 font-semibold">{`Welcome to ${pub.displayTitle || pub.title}`}</h2>
|
||||||
<Posts allPosts={allPosts}/>
|
<Posts allPosts={allPosts}/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,10 +4,13 @@ import Posts from '../../../components/Posts.astro';
|
||||||
import {getAllPosts} from '../../../hn-gql';
|
import {getAllPosts} from '../../../hn-gql';
|
||||||
import Taged from '../../../components/Tag.astro';
|
import Taged from '../../../components/Tag.astro';
|
||||||
|
|
||||||
|
export const prerender = true
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const data = await getAllPosts();
|
const data = await getAllPosts();
|
||||||
const allPosts = data.publication.posts.edges;
|
const allPosts = data.publication.posts.edges;
|
||||||
|
|
||||||
|
// biome-ignore lint/complexity/useFlatMap: <explanation>
|
||||||
const allTags = [...new Set(allPosts.map((post) => post.node.tags).flat())];
|
const allTags = [...new Set(allPosts.map((post) => post.node.tags).flat())];
|
||||||
const jsonObject = allTags.map((object) => JSON.stringify(object));
|
const jsonObject = allTags.map((object) => JSON.stringify(object));
|
||||||
const uniqueSet = new Set(jsonObject);
|
const uniqueSet = new Set(jsonObject);
|
||||||
|
@ -15,8 +18,10 @@ export async function getStaticPaths() {
|
||||||
|
|
||||||
return uniqueTags.map((uTag) => {
|
return uniqueTags.map((uTag) => {
|
||||||
const filteredPosts: { node: { author: { name: string; profilePicture: string; }; publishedAt: string; title: string; subtitle: string; brief: string; slug: string; readTimeInMinutes: number; content: { html: string; }; tags: { name: string; slug: string; }[]; coverImage: { url: string; }; }; }[] = [];
|
const filteredPosts: { node: { author: { name: string; profilePicture: string; }; publishedAt: string; title: string; subtitle: string; brief: string; slug: string; readTimeInMinutes: number; content: { html: string; }; tags: { name: string; slug: string; }[]; coverImage: { url: string; }; }; }[] = [];
|
||||||
|
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||||
allPosts.forEach((post) => {
|
allPosts.forEach((post) => {
|
||||||
const tags = post.node.tags;
|
const tags = post.node.tags;
|
||||||
|
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||||
tags.forEach((tag) => {
|
tags.forEach((tag) => {
|
||||||
if(tag.slug === uTag.slug) {
|
if(tag.slug === uTag.slug) {
|
||||||
filteredPosts.push(post)
|
filteredPosts.push(post)
|
||||||
|
|
|
@ -13,8 +13,14 @@ import background from "../assets/blog.jpg";
|
||||||
height={1080}
|
height={1080}
|
||||||
width={1920}
|
width={1920}
|
||||||
loading="eager"
|
loading="eager"
|
||||||
|
transition:name={'background:' + background}
|
||||||
|
transition:animate={'fade'}
|
||||||
/>
|
/>
|
||||||
<div class="absolute p-2 flex flex-col justify-center items-center z-10 bg-purple-50 lg:w-2/5 h-1/4 rounded-md">
|
<div
|
||||||
|
class="absolute p-2 flex flex-col justify-center items-center z-10 bg-purple-50 lg:w-2/5 h-1/4 rounded-md"
|
||||||
|
transition:name={'banner'}
|
||||||
|
transition:animate={'fade'}
|
||||||
|
>
|
||||||
<div class="flex pb-5 mb-5 text-5xl text-purple-800">
|
<div class="flex pb-5 mb-5 text-5xl text-purple-800">
|
||||||
<p>Hashnode Blog</p>
|
<p>Hashnode Blog</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
---
|
||||||
|
import { Layout } from "virtual:astro-hashnode/components";
|
||||||
|
import { getPost } from '../../hn-gql';
|
||||||
|
import Tag from '../../components/Tag.astro';
|
||||||
|
import Author from '../../components/Author.astro';
|
||||||
|
import { Markup } from 'astro-remote';
|
||||||
|
import { Image } from 'astro:assets';
|
||||||
|
import RemoteImage from '../../components/astro-remote/RemoteImage.astro';
|
||||||
|
|
||||||
|
const { slug } = Astro.params;
|
||||||
|
|
||||||
|
const checkSlug = (slug:string|undefined) => {
|
||||||
|
if (slug !== undefined) {
|
||||||
|
return slug as string;
|
||||||
|
}
|
||||||
|
return " " as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const post = await getPost(checkSlug(slug));
|
||||||
|
---
|
||||||
|
{post ? (
|
||||||
|
<Layout pageTitle={post.title} ogImage={post.coverImage.url}>
|
||||||
|
<article class="bg-white p-3 mt-3 flex flex-col">
|
||||||
|
<Image
|
||||||
|
class="rounded-lg"
|
||||||
|
src={post.coverImage.url}
|
||||||
|
alt={post.title}
|
||||||
|
inferSize={true}
|
||||||
|
loading="eager"
|
||||||
|
transition:name={'hero:' + post.coverImage.url}
|
||||||
|
/>
|
||||||
|
<h1 class="text-4xl font-bold pt-5">{post.title}</h1>
|
||||||
|
<h2 class="text-xl pt-3 pb-3" aria-label="CoverPhoto Subtitle">{post.subtitle}</h2>
|
||||||
|
|
||||||
|
<Author post={post} />
|
||||||
|
|
||||||
|
<div class="flex flex-wrap justify-center items-center mt-5 mb-5">
|
||||||
|
{post.tags && post.tags.map((tag) => <Tag tag={tag} />)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post-details">
|
||||||
|
<Markup
|
||||||
|
content={post.content.html}
|
||||||
|
components={{
|
||||||
|
img: RemoteImage
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</Layout>) : (
|
||||||
|
<Layout pageTitle="404">
|
||||||
|
<div class="text-center my-20">
|
||||||
|
<h1 class="text-4xl font-bold">404</h1>
|
||||||
|
<p>Post not found</p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
)}
|
|
@ -1,7 +1,4 @@
|
||||||
import { z } from "astro/zod";
|
import { z } from "astro/zod";
|
||||||
import { createResolver } from 'astro-integration-kit';
|
|
||||||
|
|
||||||
const { resolve } = createResolver(import.meta.url)
|
|
||||||
|
|
||||||
export function LayoutConfigSchema() {
|
export function LayoutConfigSchema() {
|
||||||
return z
|
return z
|
||||||
|
@ -9,9 +6,29 @@ export function LayoutConfigSchema() {
|
||||||
.optional()
|
.optional()
|
||||||
}
|
}
|
||||||
export const optionsSchema = z.object({
|
export const optionsSchema = z.object({
|
||||||
|
/**
|
||||||
|
* The URL of the Hashnode blog
|
||||||
|
*/
|
||||||
hashnodeURL: z.string(),
|
hashnodeURL: z.string(),
|
||||||
|
/**
|
||||||
|
* Allows the user to disable the default landing page and use their own Astro site instead of a landing page.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
landingPage: z.boolean().default(true),
|
landingPage: z.boolean().default(true),
|
||||||
|
/**
|
||||||
|
* Allows the user to enable/disable the Astro ViewTransitions component.
|
||||||
|
* @default true
|
||||||
|
* @see https://docs.astro.build/en/guides/view-transitions/ for more information about ViewTransitions
|
||||||
|
*/
|
||||||
|
useViewTransitions: z.boolean().default(true),
|
||||||
|
/**
|
||||||
|
* Allows the user to change the layout component used for Astro-Hashnode pages.
|
||||||
|
*/
|
||||||
layoutComponent: LayoutConfigSchema(),
|
layoutComponent: LayoutConfigSchema(),
|
||||||
|
/**
|
||||||
|
* Allows the user to enable verbose logging
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
verbose: z.boolean().default(false),
|
verbose: z.boolean().default(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
import { defineConfig } from "astro/config";
|
import { defineConfig } from "astro/config";
|
||||||
import astroHashnode from "@matthiesenxyz/astro-hashnode";
|
import astroHashnode from "@matthiesenxyz/astro-hashnode";
|
||||||
|
// import node from '@astrojs/node';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
// output: 'server',
|
||||||
|
// adapter: node({
|
||||||
|
// mode: 'standalone',
|
||||||
|
// }),
|
||||||
integrations: [
|
integrations: [
|
||||||
astroHashnode({
|
astroHashnode({
|
||||||
hashnodeURL: "astroplayground.hashnode.dev",
|
hashnodeURL: "astroplayground.hashnode.dev",
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
disableViewTransitions: false,
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
});
|
});
|
|
@ -11,12 +11,13 @@
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro": "^4.4.15",
|
"@astrojs/node": "8.2.4",
|
||||||
"@matthiesenxyz/astro-hashnode": "workspace:*"
|
"@matthiesenxyz/astro-hashnode": "workspace:*",
|
||||||
|
"astro": "^4.5.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/check": "^0.5.7",
|
"@astrojs/check": "^0.5.9",
|
||||||
"@types/node": "^20.11.25",
|
"@types/node": "^20.11.28",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
769
pnpm-lock.yaml
769
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue