Skip to content

Commit

Permalink
Merge pull request #188 from Cloud-Code-AI/186-add-feature-to-migrate…
Browse files Browse the repository at this point in the history
…-from-gitbook-to-akiradoc

186 add feature to migrate from gitbook to akiradoc
  • Loading branch information
sauravpanda authored Dec 9, 2024
2 parents 02bb980 + be0975e commit 0809c59
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 13 deletions.
3 changes: 2 additions & 1 deletion packages/akiradocs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"dev": "npm run compile && set NEXT_PUBLIC_AKIRADOCS_EDIT_MODE=true && next dev",
"build": "npm run compile && npm run generate-sitemap && set NEXT_PUBLIC_AKIRADOCS_EDIT_MODE=false && next build",
"start": "set NEXT_PUBLIC_AKIRADOCS_EDIT_MODE=false && next start",
"lint": "next lint"
"lint": "next lint",
"migrate-gitbook": "node scripts/migrations/gitbook.js"
},
"dependencies": {
"@ai-sdk/anthropic": "^1.0.1",
Expand Down
29 changes: 28 additions & 1 deletion packages/akiradocs/scripts/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,32 @@ async function convertMarkdownToBlocks(content) {
continue;
}

if (line.trim() === '***') {
if (currentBlock.length > 0) {
blocks.push({
id: String(blockId++),
type: 'paragraph',
content: currentBlock.join('\n').trim()
});
currentBlock = [];
}

blocks.push({
id: String(blockId++),
type: 'divider',
content: ''
});
continue;
}

if (line.startsWith('#')) {
if (!firstHeadingFound) {
const match = line.match(/^#+\s*(.*)/);
if (match) {
title = match[1].trim();
if (title.includes('**')) {
title = `<strong>${title.replace(/\*\*/g, '')}</strong>`;
}
firstHeadingFound = true;
skipNextLine = true;
continue;
Expand All @@ -119,10 +140,16 @@ async function convertMarkdownToBlocks(content) {
if (!match) continue;

const level = match[0].length;
let content = line.slice(level).trim();

if (content.includes('**')) {
content = `<strong>${content.replace(/\*\*/g, '')}</strong>`;
}

blocks.push({
id: String(blockId++),
type: 'heading',
content: line.slice(level).trim(),
content: content,
metadata: { level }
});

Expand Down
125 changes: 125 additions & 0 deletions packages/akiradocs/scripts/migrations/gitbook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
const { readFile, writeFile, mkdir } = require('fs/promises');
const { glob } = require('glob');
const matter = require('gray-matter');
const path = require('path');
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');

function processGitbookContent(content) {
// Remove Gitbook-specific content references
content = content.replace(/{% content-ref url="(.*?)" %}[\s\S]*?{% endcontent-ref %}/g, '');

// Convert Gitbook links to standard markdown links
content = content.replace(/\[(.*?)\]\((.*?)\.md\)/g, (match, text, url) => {
// Remove .md extension and convert to relative path
const cleanUrl = url.replace(/\.md$/, '');
return `[${text}](${cleanUrl})`;
});

return content;
}

async function migrateGitbookContent(options) {
const {
inputDir,
outputDir = '_contents',
language = 'en',
defaultAuthor = 'Anonymous'
} = options;

try {
const files = await glob(`${inputDir}/**/*.md`, { cwd: process.cwd() });

if (files.length === 0) {
console.warn(`No markdown files found in ${inputDir}`);
return;
}

console.log(`Found ${files.length} files to migrate...`);

for (const file of files) {
const content = await readFile(file, 'utf-8');
const { data: frontmatter, content: markdownContent } = matter(content);

const processedContent = processGitbookContent(markdownContent);

const newFrontmatter = {
title: frontmatter.title || '',
description: frontmatter.description || '',
author: frontmatter.author || defaultAuthor,
publishDate: frontmatter.publishDate || new Date().toISOString().split('T')[0],
modifiedDate: frontmatter.modifiedDate || new Date().toISOString().split('T')[0],
category: frontmatter.category || '',
keywords: frontmatter.keywords || []
};

const newContent = matter.stringify(processedContent, newFrontmatter);

const newPath = file
.replace(new RegExp(`^${inputDir}`), path.join(outputDir, language, 'docs'))
.replace(/SUMMARY\.md$/i, 'index.md');

await mkdir(path.dirname(newPath), { recursive: true });
await writeFile(newPath, newContent);

console.log(`Migrated ${file} -> ${newPath}`);
}
} catch (error) {
console.error('Error migrating Gitbook content:', error);
process.exit(1);
}
}

async function main() {
const argv = yargs(hideBin(process.argv))
.usage('Usage: $0 <input-dir> [options]')
.positional('input-dir', {
describe: 'Input directory containing Gitbook markdown files',
type: 'string'
})
.option('output-dir', {
alias: 'o',
type: 'string',
description: 'Output directory for processed files',
default: '_contents'
})
.option('language', {
alias: 'l',
type: 'string',
description: 'Language code for the content',
default: 'en'
})
.option('author', {
alias: 'a',
type: 'string',
description: 'Default author name for content without authors',
default: 'Anonymous'
})
.demandCommand(1, 'Please specify an input directory')
.help()
.argv;

// Clean up input directory path by removing trailing slashes
const inputDir = argv._[0].replace(/\/+$/, '');

if (!inputDir) {
console.error('Input directory is required');
process.exit(1);
}

await migrateGitbookContent({
inputDir,
outputDir: argv.outputDir,
language: argv.language,
defaultAuthor: argv.author
});
}

module.exports = {
main,
migrateGitbookContent
};

if (require.main === module) {
main();
}
20 changes: 12 additions & 8 deletions packages/akiradocs/src/app/[locale]/[type]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { redirect } from 'next/navigation'
import { getAllPosts, getRecentContent } from '@/lib/content'
import { getRecentContent } from '@/lib/content'
import { getHeaderConfig } from '@/lib/headerConfig'
import { getAkiradocsConfig } from '@/lib/getAkiradocsConfig'
import { Locale } from '@/types/AkiraConfigType'

type Props = {
params: Promise<{
Expand All @@ -13,7 +15,8 @@ type Props = {
export const dynamic = 'force-static';

export async function generateStaticParams() {
const locales = ['en', 'es', 'fr'];
const akiraconfig = await getAkiradocsConfig();
const locales = akiraconfig.localization.locales.map((locale: Locale) => locale.code);
const types = ['docs', 'api', 'articles'];
const params: { locale: string, type: string }[] = [];

Expand All @@ -30,17 +33,18 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
const resolvedParams = await Promise.resolve(params);
const { locale, type } = resolvedParams;
const headerConfig = getHeaderConfig();

const akiraconfig = await getAkiradocsConfig();
return {
title: `${type.charAt(0).toUpperCase() + type.slice(1)} - ${headerConfig.title}`,
description: `Browse our latest ${type}`,
alternates: {
canonical: `${process.env.NEXT_PUBLIC_SITE_URL}/${locale}/${type}`,
languages: {
'en': `${process.env.NEXT_PUBLIC_SITE_URL}/en/${type}`,
'es': `${process.env.NEXT_PUBLIC_SITE_URL}/es/${type}`,
'fr': `${process.env.NEXT_PUBLIC_SITE_URL}/fr/${type}`,
}
languages: Object.fromEntries(
akiraconfig.localization.locales.map((locale: Locale) => [
locale.code,
`${process.env.NEXT_PUBLIC_SITE_URL}/${locale.code}/${type}`
])
)
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions packages/akiradocs/src/lib/renderers/BlockRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,20 @@ export function BlockRenderer({ block, isEditing, onUpdate }: BlockRendererProps
</Paragraph>
);
case 'heading':
const hasStrong = block.content.includes('<strong>');
return (
<HeadingTitle
{...commonProps}
level={block.metadata?.level || 1}
align={block.metadata?.align}
styles={block.metadata?.styles}
styles={{
...block.metadata?.styles,
bold: hasStrong ? true : false
}}
isEditing={isEditing}
onUpdate={(content) => onUpdate?.(block.id, content)}
>
{block.content}
{hasStrong ? block.content.replace(/<\/?strong>/g, '') : block.content}
</HeadingTitle>
);
case 'list':
Expand Down
2 changes: 1 addition & 1 deletion packages/akiradocs/src/types/AkiraConfigType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Logo = {
icon: string;
};

type Locale = {
export type Locale = {
code: string;
name: string;
flag: string;
Expand Down

0 comments on commit 0809c59

Please sign in to comment.