Skip to content
Open
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
76 changes: 76 additions & 0 deletions benchmarks/JekyllImporterBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace YiiPress\Benchmarks;

use PhpBench\Attributes\AfterMethods;
use PhpBench\Attributes\BeforeMethods;
use PhpBench\Attributes\Iterations;
use PhpBench\Attributes\Revs;
use PhpBench\Attributes\Warmup;
use YiiPress\Import\Jekyll\JekyllContentImporter;

#[BeforeMethods('setUp')]
#[AfterMethods('tearDown')]
final class JekyllImporterBench
{
private string $sourceDir;
private string $targetDir;
private JekyllContentImporter $importer;

public function setUp(): void
{
$this->sourceDir = sys_get_temp_dir() . '/yiipress-jekyll-bench-source-' . uniqid();
$this->targetDir = sys_get_temp_dir() . '/yiipress-jekyll-bench-target-' . uniqid();
mkdir($this->sourceDir . '/_posts', 0o755, true);
mkdir($this->targetDir, 0o755, true);

for ($i = 1; $i <= 100; $i++) {
file_put_contents(
$this->sourceDir . '/_posts/2024-03-' . str_pad((string) (($i % 28) + 1), 2, '0', STR_PAD_LEFT) . '-post-' . $i . '.md',
"---\ntitle: Post $i\ntags: [php, yii]\n---\n\nBody $i.\n",
);
}

$this->importer = new JekyllContentImporter();
}

public function tearDown(): void
{
$this->removeDir($this->sourceDir);
$this->removeDir($this->targetDir);
}

#[Revs(10)]
#[Iterations(3)]
#[Warmup(1)]
public function benchImportPosts(): void
{
$this->removeDir($this->targetDir);
mkdir($this->targetDir, 0o755, true);

$this->importer->import(['directory' => $this->sourceDir], $this->targetDir, 'blog');
}

private function removeDir(string $path): void
{
if (!is_dir($path)) {
return;
}

$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST,
);
foreach ($iterator as $item) {
if ($item->isDir()) {
rmdir($item->getPathname());
} else {
unlink($item->getPathname());
}
}

rmdir($path);
}
}
2 changes: 2 additions & 0 deletions config/common/di/importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);

use YiiPress\Console\ImportCommand;
use YiiPress\Import\Jekyll\JekyllContentImporter;
use YiiPress\Import\Telegram\TelegramContentImporter;

$workingDirectory = getcwd() ?: dirname(__DIR__, 3);
Expand All @@ -12,6 +13,7 @@
'__construct()' => [
'rootPath' => $workingDirectory,
'importers' => [
'jekyll' => new JekyllContentImporter(),
'telegram' => new TelegramContentImporter(),
],
],
Expand Down
27 changes: 26 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Imports content from external sources into a YiiPress collection.

**Arguments:**

- `source` — source type to import from (required). Currently supported: `telegram`.
- `source` — source type to import from (required). Currently supported: `jekyll`, `telegram`.

**Common options:**

Expand Down Expand Up @@ -191,6 +191,31 @@ Supports both single-chat exports (`result.json` with `messages` array) and full
./yiipress import telegram --directory=./telegram-data --content-dir=content
```

### Jekyll import

Imports Markdown posts from a Jekyll site directory. The importer reads `_posts/YYYY-MM-DD-slug.md` and `_posts/YYYY-MM-DD-slug.markdown` files, converts common front matter fields, and writes YiiPress markdown files into the target collection.

**Importer options:**

- `--directory` — path to the Jekyll site directory containing `_posts` (required). Absolute or relative to project root.

The importer preserves:

- `title`
- `date`
- `permalink`
- `tags`
- `categories`

If `title` is missing, it is inferred from the first `# Heading` in the post body and then from the filename slug.

**Examples:**

```bash
./yiipress import jekyll --directory=/path/to/jekyll-site
./yiipress import jekyll --directory=../old-blog --collection=blog
```

### Adding custom importers

Importers implement `YiiPress\Import\ContentImporterInterface` and are registered via [Yii3 DI](https://yiisoft.github.io/docs/guide/concept/di-container.html) in `config/common/di/importer.php`. Each importer declares its own options via the `options()` method. See [Importing content](importing-content.md) for details.
Expand Down
12 changes: 12 additions & 0 deletions docs/importing-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ Imports messages from a Telegram Desktop channel export (JSON format).

See [commands.md](commands.md#yii-import) for usage details.

### JekyllContentImporter

Imports Markdown posts from a Jekyll site `_posts/` directory.

**Options:**

- `--directory` — Path to the Jekyll site directory containing `_posts` (required)

The importer accepts `.md` and `.markdown` posts named `YYYY-MM-DD-slug`, preserves common front matter (`title`, `date`, `permalink`, `tags`, `categories`), and creates a default collection config when one does not exist.

See [commands.md](commands.md#jekyll-import) for usage details.

## Writing a custom importer

Create a class implementing `ContentImporterInterface`. Each importer declares its own options — a file-based importer might need a `directory`, while an API-based importer might need `url` and `api-key`.
Expand Down
2 changes: 1 addition & 1 deletion roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
## Priority 9: Data importers

- [ ] WordPress
- [ ] Jekyll
- [x] Jekyll
- [ ] Hugo
- [ ] Medium exported Markdown
- [ ] Ghost
Expand Down
Loading