Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds auto generate showcases #1296

Merged
merged 9 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ For proper integration with the starter-dev CLI and website there are also some
- `description` - This field is used as a short description highlighting the technologies of each particular starter kit. It is shown as a selection in the starter-dev CLI. Example: `NextJS, React Query, and TailwindCSS`
- `keywords` - This field is an array of strings used to map to the technology list on the starter.dev website. Example: `["next", "react-query", "tailwind"]`
- `hasShowcase` - This field is a boolean and is required when displaying the "View Showcase" link for kits that have showcases. If your kit has a showcase, set the value to `true`. If your kit does not have a showcase, set the value to `false`.
- `showcase` - This field is an array and is required when displaying the showcase list on individual kit pages. If your kit has a showcase, add an object containing `kit`, `name`, `repo`, and `app` to the array. Please check existing package.jsons like the one found here starters/svelte-kit-scss/package.json for what the values should be.

### Adding starter kit to the website

Expand Down
1 change: 1 addition & 0 deletions packages/website/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ yarn-error.log*
# ignore generated kit pages
src/pages/kits/*.md
src/pages/kits/*.mdx
src/showcases/*.json
6 changes: 4 additions & 2 deletions packages/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
"format": "yarn prettier -w .",
"lint": "eslint ./src",
"kitgen": "node ./scripts/generate-kit-pages.js",
"predev": "yarn kitgen",
"prebuild": "yarn kitgen"
"showcasegen": "node ./scripts/generate-showcases.js",
"predev": "yarn kitgen && yarn showcasegen",
"prebuild": "yarn kitgen && yarn showcasegen"
},
"devDependencies": {
"@astrojs/mdx": "^0.13.0",
Expand All @@ -33,6 +34,7 @@
"@heroicons/react": "^1.0.5",
"autoprefixer": "^10.4.2",
"clsx": "^1.1.1",
"json-loader": "^0.5.7",
"just-pick": "^4.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
93 changes: 2 additions & 91 deletions packages/website/scripts/generate-kit-pages.js
Original file line number Diff line number Diff line change
@@ -1,98 +1,9 @@
const path = require('path');
const fs = require('fs/promises');
const pick = require('just-pick');
const rimraf = require('rimraf');
const { getRepoRootPath, getKitDirs } = require('../../../scripts/kits-utils');

const KIT_PAGE_RELATIVE_LAYOUT_PATH = '../../layouts/KitLayout.astro';

function convertToFrontmatter(obj) {
return Object.entries(obj)
.map(([key, value]) => `${key}: ${obj[key]}`)
.join('\n');
}

function formatMarkdownFile(markdown, frontmatter) {
return `---
# generated by scripts/kitgen at ${new Date().toISOString()}
# edit KIT_PAGE_RELATIVE_LAYOUT_PATH in scripts/kitgen to change layout
layout: ${KIT_PAGE_RELATIVE_LAYOUT_PATH}
${frontmatter}
---

${markdown}`;
}
const { generate } = require('./generate');

///////////////////////////////////////////////////////////////////////////////
// SCRIPT MAIN
////////////////////////////////////////////////////////////////////////////////

(async () => {
const repoPath = getRepoRootPath();
const kitPagesPath = path.join(repoPath, 'packages/website/src/pages/kits');

try {
const handle = await fs.opendir(kitPagesPath);
console.info('kitgen: deleting existing kit pages');
rimraf(`${kitPagesPath}/*.md,${kitPagesPath}/*.mdx`, async (err) => {
if (err) {
console.error(err);
}
await handle.close();
});
} catch (err) {
if (err && err.code === 'ENOENT') {
console.info('kitgen: creating kit pages directory');
await fs.mkdir(kitPagesPath);
}
}

const kitDirs = await getKitDirs();

console.info('kitgen: generating kit pages');
for (const dir of kitDirs) {
const kitPath = path.join(repoPath, 'starters', dir);

let infoFile = 'package.json';
if (dir.startsWith('deno-')) {
// For Deno, we don't have package.json
infoFile = 'deno.json';
}

try {
const readme = await fs.readFile(
path.join(kitPath, 'README.md'),
'utf-8'
);
const json = await fs.readFile(path.join(kitPath, infoFile), 'utf-8');
const data = JSON.parse(json);

const kitData = {
...pick(data, [
'name',
'version',
'description',
'keywords',
'hasShowcase',
]),
readmePath: path.join(kitPath, 'README.md'),
starterPath: `/starters/${dir}`,
};

const frontmatter = convertToFrontmatter(kitData);
const formattedMarkdown = formatMarkdownFile(readme, frontmatter);

const kitPagePath = path.join(
repoPath,
'packages/website/src/pages/kits',
`${data.name}.mdx`
);

await fs.writeFile(kitPagePath, formattedMarkdown, 'utf-8');
} catch (err) {
console.error(
`KITGEN: failed to write kit page for ${dir}: ${err.message}`
);
}
}
await generate(true, 'packages/website/src/pages/kits');
})();
8 changes: 8 additions & 0 deletions packages/website/scripts/generate-showcases.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { generate } = require('./generate');
///////////////////////////////////////////////////////////////////////////////
// SCRIPT MAIN
////////////////////////////////////////////////////////////////////////////////

(async () => {
await generate(false, 'packages/website/src/showcases');
})();
137 changes: 137 additions & 0 deletions packages/website/scripts/generate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const path = require('path');
const fs = require('fs/promises');
const pick = require('just-pick');
const rimraf = require('rimraf');
const { getRepoRootPath, getKitDirs } = require('../../../scripts/kits-utils');

const KIT_PAGE_RELATIVE_LAYOUT_PATH = '../../layouts/KitLayout.astro';

function convertToFrontmatter(obj) {
return Object.entries(obj)
.map(([key, value]) => `${key}: ${obj[key]}`)
.join('\n');
}

function formatMarkdownFile(markdown, frontmatter) {
return `---
# generated by scripts/kitgen at ${new Date().toISOString()}
# edit KIT_PAGE_RELATIVE_LAYOUT_PATH in scripts/kitgen to change layout
layout: ${KIT_PAGE_RELATIVE_LAYOUT_PATH}
${frontmatter}
---

${markdown}`;
}

async function createDeleteFiles(isKit, PagesPath) {
try {
const handle = await fs.opendir(PagesPath);
console.info(
isKit
? 'kitgen: deleting existing kit pages'
: 'showcasegen: deleting existing showcase file'
);
rimraf(
isKit ? `${PagesPath}/*.md,${PagesPath}/*.mdx` : `${PagesPath}/*`,
async (err) => {
if (err) {
console.error(err);
}
await handle.close();
}
);
} catch (err) {
if (err && err.code === 'ENOENT') {
console.info(
isKit
? 'kitgen: creating kit pages directory'
: 'showcasegen: creating showcase file'
);
await fs.mkdir(PagesPath);
}
}
}

async function generate(isKit, genPath) {
const repoPath = getRepoRootPath();
const PagesPath = path.join(repoPath, genPath);
await createDeleteFiles(isKit, PagesPath);

const kitDirs = await getKitDirs();
const showcases = [];
let pickData;
let formattedMarkdown;
let PagePath;

console.info(
isKit ? 'kitgen: generating kit pages' : 'showcasegen: generating showcases'
);
for (const dir of kitDirs) {
const kitPath = path.join(repoPath, 'starters', dir);

let infoFile = 'package.json';
if (dir.startsWith('deno-')) {
// For Deno, we don't have package.json
infoFile = 'deno.json';
}

try {
const readme = await fs.readFile(
path.join(kitPath, 'README.md'),
'utf-8'
);
const json = await fs.readFile(path.join(kitPath, infoFile), 'utf-8');
const data = JSON.parse(json);

if (isKit) {
pickData = {
...pick(data, [
'name',
'version',
'description',
'keywords',
'hasShowcase',
]),
readmePath: path.join(kitPath, 'README.md'),
starterPath: `/starters/${dir}`,
};
const frontmatter = convertToFrontmatter(pickData);
formattedMarkdown = formatMarkdownFile(readme, frontmatter);
PagePath = path.join(
repoPath,
'packages/website/src/pages/kits',
`${data.name}.mdx`
);
} else {
pickData = {
...pick(data, ['showcases']),
};
Object.entries(pickData).map(([key, value]) => {
value.map((item) => {
showcases.push(item);
});
});
PagePath = path.join(
repoPath,
'packages/website/src/showcases/showcases.json'
);
}

await fs.writeFile(
PagePath,
isKit ? formattedMarkdown : JSON.stringify(showcases),
'utf-8'
);
} catch (err) {
console.error(
isKit
? `KITGEN: failed to write kit page for ${dir}: ${err.message}`
: `SHOWCASEGEN: failed to write showcase page: ${err.message}`
);
}
}
}

module.exports = {
generate,
};
7 changes: 5 additions & 2 deletions packages/website/src/components/KitActionLinks.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { name, hasShowcase, inline } = Astro.props;
---

<div class="flex flex-row items-center justify-center max-w-screen-2xl mx-auto">
<div class=" hidden lg:block invisible lg:visible lg:space-x-4 ">
<div class="hidden lg:block invisible lg:visible lg:space-x-4">
<ShareDropdown kitname={name} client:only />
<a
class="link text-sm dark:dark-link hover:underline"
Expand All @@ -32,7 +32,10 @@ const { name, hasShowcase, inline } = Astro.props;
}

<a
class={cn('btn btn-primary text-sm mt-3 ml-3 mb-3 lg:mb-auto', !inline && 'mt-3.5')}
class={cn(
'btn btn-primary text-sm mt-3 ml-3 mb-3 lg:mb-auto',
!inline && 'mt-3.5'
)}
href={`${REPO_URL}/tree/main/starters/${name}`}
target="_blank"
rel="noopener noreferrer"
Expand Down
12 changes: 7 additions & 5 deletions packages/website/src/components/KitShowcases.astro
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
---
import ShowcaseLinks from './ShowcaseLinks.astro';
import ShowcaseIcon from './ShowcaseIcon.tsx';
const { showcases, hasShowcase } = Astro.props;
---

{
hasShowcase ? (
<div id="showcase-section" class="pt-24 max-w-screen-2xl mx-auto scroll-mt-20">
<div
id="showcase-section"
class="pt-24 max-w-screen-2xl mx-auto scroll-mt-20"
>
<h1 class="heading-1 font-medium mb-8">
See kits in action via our showcases
</h1>
<div class="not-prose flex flex-col sm:col-span-1 md:flex-row flex-wrap gap-4">
{showcases.map(({ name, repo, app, Icon }) => (
{showcases.map(({ name, repo, app, icon }) => (
<div class="border bg-gray-100 dark:bg-gray-800 group-hover:bg-blue-500 border-gray-400 dark:border-gray-700 rounded-xl items-center t-dark dark:dark-t-light p-4 ">
<div class="flex items-center pb-4">
<div class="justify-center p-1.5 border dark:bg-gray-800 group-hover:bg-blue-500 border-gray-400 dark:border-gray-700 rounded-xl flex items-center font-medium justify-start min-w-[48px] min-h-[48px]">
<Icon className="inline w-6 h-6 mb-px" />
</div>
<ShowcaseIcon icon={icon} />
<div class="mx-4">
<p class="text-base t-dark dark:dark-t-light group-hover:text-white leading-snug">
{name}
Expand Down
26 changes: 26 additions & 0 deletions packages/website/src/components/ShowcaseIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { GitHubIcon, BriefcaseIcon } from '../icons';

function getIcon(icon) {
let Icon;
switch (icon) {
case 'GitHub':
Icon = <GitHubIcon className="inline w-6 h-6 mb-px" />;
break;
default:
Icon = <BriefcaseIcon className="inline w-6 h-6 mb-px" />;
}

return Icon;
}

interface Props {
icon?: string;
}

export default function ShowcaseIcon({ icon }: Props) {
return (
<div className="justify-center p-1.5 border dark:bg-gray-800 group-hover:bg-blue-500 border-gray-400 dark:border-gray-700 rounded-xl flex items-center font-medium min-w-[48px] min-h-[48px]">
{getIcon(icon)}
</div>
);
}
Loading