sourceDisk = Storage::disk('source'); $this->filename = basename($this->path); $this->fileExtension = (str_ends_with($this->filename, '.blade.php')) ? 'blade.php' : pathinfo($this->filename, PATHINFO_EXTENSION); $this->fileExtension = str($this->fileExtension)->trim('.')->toString(); $this->fileBaseName = str($this->filename)->remove("." . $this->fileExtension)->toString(); $this->title = str($this->filename)->remove("." . $this->fileExtension)->title()->toString(); $this->slug = str($this->title)->slug(); $this->directory = dirname($this->path); $this->key = str($this->path)->remove("." . $this->fileExtension)->replace("/", ".")->lower()->toString(); if ($this->fileBaseName == "index") { $this->isIndex = true; $this->isNestedIndex = true; } $this->entityName = $this->isIndex ? basename($this->directory) : $this->slug; $this->detectCollection(); $this->fillAdditionalData(); } public function __get($name) { if (!empty($this->$name)) { return $this->name; } return $this->collectionProps[$name] ?? null; } public function detectCollection() { $key_parts = explode('.', $this->key); $collections = SiteConfiguration::collections(); // Turn collections into an array containing // the parts and the length so we can easily // filter likely matches. $collections = collect($collections)->map(function ($name) { $parts = explode('.', $name); return [ 'name' => $name, 'parts' => $parts, 'length' => count($parts) ]; }) ->filter(fn($col) => $col['length'] <= count($key_parts)) ->sortBy('length'); foreach ($collections as $c) { $parts = $c['parts']; // Get a subset of max length key parts $key_compare = array_slice($key_parts, 0, count($parts)); $props = []; $match = true; foreach ($key_compare as $i => $v) { $check = $parts[$i]; // Check if it's a parameter like [project_name] if (str($check)->isMatch('/\[[a-z_]+\]/')) { $key = str($check)->remove(['[', ']'])->toString(); $props[$key] = $v; continue; } if ($v !== $check) { $match = false; break; } } if ($match) { $this->collectionHierarchy[] = $c['name']; $this->collection = $c['name']; $this->collectionProps = $props; } } // After detecting the collection, we can also // determine if the file is an index in that // collection. Example: // collection = projects // file path = projects/myproject.md if (str($this->key)->remove($this->collection)->remove(".") == $this->fileBaseName) { $this->isIndex = true; } } public function applyCollectionSettings(string $collection) { $config = SiteConfiguration::getConfig()['collections'][$collection]; $fill = ['layout']; foreach ($fill as $field) { $this->$field = $config[$field]; } } /** * Grab data from the file's front matter * as well as the config and overwrite * or fill in any missing attributes. * * @return void */ public function fillAdditionalData() { // Apply collection settings in a hierarchical // manner, with the settings from the most // matched collection taking precedence // over the least. foreach ($this->collectionHierarchy as $collection) { $this->applyCollectionSettings($collection); } // After the hierarchy, also apply settings // from the detected collection to be // safe. if (!empty($this->collection)) { $this->applyCollectionSettings($collection); } // Grab front matter data if any. Apply any settings // from it since it takes precedence over // collection settings. if ($this->fileExtension == 'md') { // Build the markdown parser $mdEnvironment = new Environment(); $mdEnvironment->addExtension(new CommonMarkCoreExtension); $mdEnvironment->addExtension(new FrontMatterExtension); $parser = new MarkdownConverter($mdEnvironment); $parsed = $parser->convert($this->content()); if ($parsed instanceof RenderedContentWithFrontMatter) { $frontMatter = $parsed->getFrontMatter(); foreach ($frontMatter as $field => $value) { $this->$field = $value; } } } // If layout is still empty just set it to main if (empty($this->layout)) $this->layout = 'main'; } public function renderer(): Renderer { return ($this->fileExtension == 'blade.php') ? new BladeRenderer : new MarkdownRenderer; } public function render(): string { return $this->renderer()->render($this); } public function outputPath(): string { $baseDirectory = $this->directory; if ($this->isIndex) { return join_paths($baseDirectory, "index.html"); } return join_paths($baseDirectory, "{$this->slug}", "index.html"); } public function url(): string { $baseDirectory = $this->directory; if ($this->isIndex && $this->isNestedIndex) { return str($this->directory)->prepend("/"); } return str(join_paths($baseDirectory, "{$this->slug}"))->prepend("/"); } public function content(): string { if (!empty($this->content)) { return $this->content; } $content = Storage::disk('source')->get($this->path); $this->content = $content; return $content; } }