docyard 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- data/.github/pull_request_template.md +14 -0
- data/.github/workflows/ci.yml +49 -0
- data/.rubocop.yml +38 -0
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +55 -0
- data/README.md +174 -3
- data/lib/docyard/asset_handler.rb +64 -0
- data/lib/docyard/cli.rb +34 -0
- data/lib/docyard/constants.rb +23 -0
- data/lib/docyard/errors.rb +54 -0
- data/lib/docyard/file_watcher.rb +42 -0
- data/lib/docyard/initializer.rb +76 -0
- data/lib/docyard/logging.rb +43 -0
- data/lib/docyard/markdown.rb +61 -0
- data/lib/docyard/rack_application.rb +81 -0
- data/lib/docyard/renderer.rb +61 -0
- data/lib/docyard/router.rb +31 -0
- data/lib/docyard/routing/resolution_result.rb +31 -0
- data/lib/docyard/server.rb +91 -0
- data/lib/docyard/sidebar/file_system_scanner.rb +77 -0
- data/lib/docyard/sidebar/renderer.rb +110 -0
- data/lib/docyard/sidebar/title_extractor.rb +25 -0
- data/lib/docyard/sidebar/tree_builder.rb +59 -0
- data/lib/docyard/sidebar_builder.rb +50 -0
- data/lib/docyard/templates/assets/css/code.css +214 -0
- data/lib/docyard/templates/assets/css/components.css +258 -0
- data/lib/docyard/templates/assets/css/layout.css +273 -0
- data/lib/docyard/templates/assets/css/main.css +10 -0
- data/lib/docyard/templates/assets/css/markdown.css +199 -0
- data/lib/docyard/templates/assets/css/reset.css +59 -0
- data/lib/docyard/templates/assets/css/typography.css +97 -0
- data/lib/docyard/templates/assets/css/variables.css +114 -0
- data/lib/docyard/templates/assets/js/reload.js +98 -0
- data/lib/docyard/templates/assets/js/theme.js +193 -0
- data/lib/docyard/templates/errors/404.html.erb +16 -0
- data/lib/docyard/templates/errors/500.html.erb +25 -0
- data/lib/docyard/templates/layouts/default.html.erb +51 -0
- data/lib/docyard/templates/markdown/core-concepts/file-structure.md.erb +61 -0
- data/lib/docyard/templates/markdown/core-concepts/markdown.md.erb +90 -0
- data/lib/docyard/templates/markdown/getting-started/installation.md.erb +43 -0
- data/lib/docyard/templates/markdown/getting-started/introduction.md.erb +30 -0
- data/lib/docyard/templates/markdown/getting-started/quick-start.md.erb +56 -0
- data/lib/docyard/templates/markdown/index.md.erb +86 -0
- data/lib/docyard/templates/partials/_icons.html.erb +11 -0
- data/lib/docyard/templates/partials/_nav_group.html.erb +7 -0
- data/lib/docyard/templates/partials/_nav_item.html.erb +3 -0
- data/lib/docyard/templates/partials/_nav_leaf.html.erb +1 -0
- data/lib/docyard/templates/partials/_nav_list.html.erb +3 -0
- data/lib/docyard/templates/partials/_nav_section.html.erb +6 -0
- data/lib/docyard/templates/partials/_sidebar.html.erb +6 -0
- data/lib/docyard/templates/partials/_sidebar_footer.html.erb +11 -0
- data/lib/docyard/utils/path_resolver.rb +30 -0
- data/lib/docyard/utils/text_formatter.rb +22 -0
- data/lib/docyard/version.rb +1 -1
- data/lib/docyard.rb +20 -2
- metadata +169 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0614d285d89d4f529607c5e4bdb50c823cce769a5628b90100234c9513f98690
|
|
4
|
+
data.tar.gz: 2f7c134bca20f17766e54fb4bc3b02b602e735cf4d8c7e11f83f9158773bec22
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1259470e5245ff3ffc8796144a1e48fae46d851b44ca2c6cbeda0e8f83c782ebb815cfdfe0bdb3fa36fc25744a050859df44e5451376c8dadb4601ad9c3e2e98
|
|
7
|
+
data.tar.gz: 90281b4af9dd20425d26d6ea14214de5a79237ddeec9038546b9142819e363051850c64942a1b56459e5e8e1c509d08a36b323c2e01e3bacd4353fc39755915a
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug Report
|
|
3
|
+
about: Report a bug
|
|
4
|
+
title: ''
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
|
|
11
|
+
<!-- Clear description of the bug -->
|
|
12
|
+
|
|
13
|
+
## Steps to Reproduce
|
|
14
|
+
|
|
15
|
+
1.
|
|
16
|
+
2.
|
|
17
|
+
3.
|
|
18
|
+
|
|
19
|
+
## Expected Behavior
|
|
20
|
+
|
|
21
|
+
<!-- What should happen -->
|
|
22
|
+
|
|
23
|
+
## Actual Behavior
|
|
24
|
+
|
|
25
|
+
<!-- What actually happens -->
|
|
26
|
+
|
|
27
|
+
## Environment
|
|
28
|
+
|
|
29
|
+
- Ruby version: <!-- `ruby -v` -->
|
|
30
|
+
- Docyard version: <!-- `gem list docyard` -->
|
|
31
|
+
- OS: <!-- macOS, Linux, etc. -->
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature Request
|
|
3
|
+
about: Suggest a feature
|
|
4
|
+
title: ''
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
|
|
11
|
+
<!-- What feature would you like? -->
|
|
12
|
+
|
|
13
|
+
## Use Case
|
|
14
|
+
|
|
15
|
+
<!-- Why is this useful? When would you use it? -->
|
|
16
|
+
|
|
17
|
+
## Implementation Ideas
|
|
18
|
+
|
|
19
|
+
<!-- Optional: How might this work? -->
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Ruby ${{ matrix.ruby }} - Test
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
ruby: ['3.2', '3.3', '3.4', 'head']
|
|
17
|
+
continue-on-error: ${{ matrix.ruby == 'head' }}
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout code
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Set up Ruby
|
|
24
|
+
uses: ruby/setup-ruby@v1
|
|
25
|
+
with:
|
|
26
|
+
ruby-version: ${{ matrix.ruby }}
|
|
27
|
+
bundler-cache: true
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: bundle exec rspec
|
|
31
|
+
env:
|
|
32
|
+
RUBYOPT: '-W0'
|
|
33
|
+
|
|
34
|
+
lint:
|
|
35
|
+
name: Rubocop
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout code
|
|
40
|
+
uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Set up Ruby
|
|
43
|
+
uses: ruby/setup-ruby@v1
|
|
44
|
+
with:
|
|
45
|
+
ruby-version: '3.3'
|
|
46
|
+
bundler-cache: true
|
|
47
|
+
|
|
48
|
+
- name: Run rubocop
|
|
49
|
+
run: bundle exec rubocop
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-rspec
|
|
3
|
+
- rubocop-rake
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
NewCops: enable
|
|
7
|
+
TargetRubyVersion: 3.2
|
|
8
|
+
Exclude:
|
|
9
|
+
- 'vendor/**/*'
|
|
10
|
+
- 'spec/fixtures/**/*'
|
|
11
|
+
- 'tmp/**/*'
|
|
12
|
+
|
|
13
|
+
Metrics/BlockLength:
|
|
14
|
+
Exclude:
|
|
15
|
+
- 'spec/**/*'
|
|
16
|
+
- '*.gemspec'
|
|
17
|
+
|
|
18
|
+
RSpec/ExampleLength:
|
|
19
|
+
Max: 10
|
|
20
|
+
|
|
21
|
+
Metrics/ClassLength:
|
|
22
|
+
Max: 150
|
|
23
|
+
|
|
24
|
+
Metrics/MethodLength:
|
|
25
|
+
Max: 15
|
|
26
|
+
|
|
27
|
+
Style/Documentation:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Layout/LineLength:
|
|
31
|
+
Max: 120
|
|
32
|
+
|
|
33
|
+
Style/StringLiterals:
|
|
34
|
+
Enabled: true
|
|
35
|
+
EnforcedStyle: double_quotes
|
|
36
|
+
|
|
37
|
+
Layout/MultilineMethodCallIndentation:
|
|
38
|
+
EnforcedStyle: indented
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.0] - 2025-11-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Automatic sidebar navigation with nested folders
|
|
14
|
+
- Collapsible sidebar sections with active page highlighting
|
|
15
|
+
- Sidebar scroll position persistence
|
|
16
|
+
- Modern responsive theme with mobile hamburger menu
|
|
17
|
+
- Structured logging system with configurable levels
|
|
18
|
+
- Custom error classes for better error handling
|
|
19
|
+
- Constants module for shared application values
|
|
20
|
+
- Utility modules for path resolution and text formatting
|
|
21
|
+
- Improved initial templates with nested docs structure
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- Refactored sidebar rendering to use partial templates
|
|
25
|
+
- Modular CSS architecture (split into variables, reset, typography, components, layout)
|
|
26
|
+
- Enhanced router with routing resolution result pattern
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
- Legacy syntax.css in favor of modular code.css
|
|
30
|
+
|
|
31
|
+
## [0.1.0] - 2025-11-04
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Hot reload
|
|
35
|
+
- GitHub Flavored Markdown support (tables, task lists, strikethrough)
|
|
36
|
+
- Syntax highlighting for 100+ languages via Rouge
|
|
37
|
+
- YAML frontmatter for page metadata
|
|
38
|
+
- Customizable 404/500 error templates
|
|
39
|
+
- CLI commands: `docyard init`, `docyard serve`
|
|
40
|
+
- File watcher for live reload
|
|
41
|
+
- Directory traversal protection in asset handler
|
|
42
|
+
|
|
43
|
+
## [0.0.1] - 2025-11-03
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- Initial gem structure
|
|
47
|
+
- Project scaffolding
|
|
48
|
+
|
|
49
|
+
[Unreleased]: https://github.com/sanifhimani/docyard/compare/v0.2.0...HEAD
|
|
50
|
+
[0.2.0]: https://github.com/sanifhimani/docyard/compare/v0.1.0...v0.2.0
|
|
51
|
+
[0.1.0]: https://github.com/sanifhimani/docyard/compare/v0.0.1...v0.1.0
|
|
52
|
+
[0.0.1]: https://github.com/sanifhimani/docyard/releases/tag/v0.0.1
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for wanting to contribute to Docyard!
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/YOUR_USERNAME/docyard.git
|
|
9
|
+
cd docyard
|
|
10
|
+
bundle install
|
|
11
|
+
|
|
12
|
+
# Run tests
|
|
13
|
+
bundle exec rspec
|
|
14
|
+
bundle exec rubocop
|
|
15
|
+
|
|
16
|
+
# Test locally
|
|
17
|
+
./bin/docyard init
|
|
18
|
+
./bin/docyard serve
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Pull Requests
|
|
22
|
+
|
|
23
|
+
1. Fork the repo and create a branch from `main`
|
|
24
|
+
2. Write tests for new features
|
|
25
|
+
3. Run `bundle exec rspec && bundle exec rubocop`
|
|
26
|
+
4. Update README/CHANGELOG if needed
|
|
27
|
+
5. Use conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
lib/docyard/
|
|
33
|
+
cli.rb # CLI
|
|
34
|
+
server.rb # Server lifecycle
|
|
35
|
+
rack_application.rb # HTTP handling
|
|
36
|
+
router.rb # URL → file mapping
|
|
37
|
+
renderer.rb # Markdown → HTML
|
|
38
|
+
markdown.rb # Parsing
|
|
39
|
+
file_watcher.rb # Live reload
|
|
40
|
+
asset_handler.rb # Static assets
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Code Style
|
|
44
|
+
|
|
45
|
+
- Maintain >85% test coverage
|
|
46
|
+
- Keep methods focused and readable
|
|
47
|
+
- Add tests for new features
|
|
48
|
+
|
|
49
|
+
## Questions?
|
|
50
|
+
|
|
51
|
+
Open an issue.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
Thanks for contributing! 🚀
|
data/README.md
CHANGED
|
@@ -1,14 +1,185 @@
|
|
|
1
1
|
# Docyard
|
|
2
2
|
|
|
3
|
+
[](https://github.com/sanifhimani/docyard/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/rb/docyard)
|
|
5
|
+
|
|
3
6
|
> Documentation generator for Ruby
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
**Early development** - Core features work, but missing search and build command. See [roadmap](#roadmap).
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Sidebar navigation** - Automatic sidebar with nested folders and collapsible sections
|
|
13
|
+
- **Hot reload** - Changes appear instantly while you write
|
|
14
|
+
- **GitHub Flavored Markdown** - Tables, task lists, strikethrough
|
|
15
|
+
- **Syntax highlighting** - 100+ languages via Rouge
|
|
16
|
+
- **YAML frontmatter** - Add metadata to your pages
|
|
17
|
+
- **Customizable error pages** - Make 404/500 pages your own
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install the gem
|
|
23
|
+
gem install docyard
|
|
24
|
+
|
|
25
|
+
# Create a new docs project
|
|
26
|
+
mkdir my-docs && cd my-docs
|
|
27
|
+
docyard init
|
|
6
28
|
|
|
7
|
-
|
|
29
|
+
# Start the dev server
|
|
30
|
+
docyard serve
|
|
31
|
+
|
|
32
|
+
# Visit http://localhost:4200
|
|
33
|
+
```
|
|
8
34
|
|
|
9
35
|
## Installation
|
|
10
36
|
|
|
11
|
-
|
|
37
|
+
Add to your Gemfile:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
gem 'docyard'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or install directly:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
gem install docyard
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### Initialize a New Project
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
docyard init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This creates:
|
|
58
|
+
```
|
|
59
|
+
docs/
|
|
60
|
+
index.md # Home page
|
|
61
|
+
getting-started/
|
|
62
|
+
introduction.md # Getting started guide
|
|
63
|
+
installation.md # Installation instructions
|
|
64
|
+
quick-start.md # Quick start guide
|
|
65
|
+
core-concepts/
|
|
66
|
+
file-structure.md # File structure guide
|
|
67
|
+
markdown.md # Markdown guide
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Start Development Server
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
docyard serve
|
|
74
|
+
|
|
75
|
+
# Custom port and host
|
|
76
|
+
docyard serve --port 3000 --host 0.0.0.0
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Writing Docs
|
|
80
|
+
|
|
81
|
+
Create markdown files in the `docs/` directory:
|
|
82
|
+
|
|
83
|
+
```markdown
|
|
84
|
+
---
|
|
85
|
+
title: Getting Started
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
# Getting Started
|
|
89
|
+
|
|
90
|
+
\`\`\`ruby
|
|
91
|
+
class User
|
|
92
|
+
def initialize(name)
|
|
93
|
+
@name = name
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
\`\`\`
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Frontmatter
|
|
100
|
+
|
|
101
|
+
Add YAML frontmatter to customize page metadata:
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
---
|
|
105
|
+
title: My Page Title
|
|
106
|
+
description: Page description
|
|
107
|
+
---
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Currently supported:
|
|
111
|
+
- `title` - Page title (shown in `<title>` tag)
|
|
112
|
+
|
|
113
|
+
### Linking Between Pages
|
|
114
|
+
|
|
115
|
+
Write links with `.md` extension, they'll be automatically cleaned:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
[Getting Started](./getting-started.md) → /getting-started
|
|
119
|
+
[Guide](./guide/index.md) → /guide
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Directory Structure
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
docs/
|
|
126
|
+
index.md # / (root)
|
|
127
|
+
getting-started.md # /getting-started
|
|
128
|
+
guide/
|
|
129
|
+
index.md # /guide
|
|
130
|
+
setup.md # /guide/setup
|
|
131
|
+
advanced.md # /guide/advanced
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Architecture
|
|
135
|
+
|
|
136
|
+
Clean separation of concerns:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
lib/docyard/
|
|
140
|
+
cli.rb # Command-line interface (Thor)
|
|
141
|
+
initializer.rb # Project scaffolding (init command)
|
|
142
|
+
server.rb # Server lifecycle (WEBrick, signals)
|
|
143
|
+
rack_application.rb # HTTP request handling (routing, rendering)
|
|
144
|
+
router.rb # URL → file path mapping
|
|
145
|
+
renderer.rb # Markdown → HTML conversion
|
|
146
|
+
markdown.rb # Markdown parsing & frontmatter extraction
|
|
147
|
+
file_watcher.rb # Live reload file monitoring
|
|
148
|
+
asset_handler.rb # Static asset serving
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Each class has a single, clear responsibility. Easy to test, easy to extend.
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
git clone https://github.com/sanifhimani/docyard.git
|
|
157
|
+
cd docyard
|
|
158
|
+
bundle install
|
|
159
|
+
|
|
160
|
+
# Run tests
|
|
161
|
+
bundle exec rspec
|
|
162
|
+
|
|
163
|
+
# Run linter
|
|
164
|
+
bundle exec rubocop
|
|
165
|
+
|
|
166
|
+
# Use local version
|
|
167
|
+
./bin/docyard init
|
|
168
|
+
./bin/docyard serve
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Roadmap
|
|
172
|
+
|
|
173
|
+
**Next up:**
|
|
174
|
+
- Dark mode with theme toggle
|
|
175
|
+
- Markdown components (callouts, code groups, collapsible sections)
|
|
176
|
+
- Icon system
|
|
177
|
+
- Client-side search
|
|
178
|
+
- Static site generation (`docyard build`)
|
|
179
|
+
|
|
180
|
+
## Contributing
|
|
181
|
+
|
|
182
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
12
183
|
|
|
13
184
|
## License
|
|
14
185
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docyard
|
|
4
|
+
class AssetHandler
|
|
5
|
+
ASSETS_PATH = File.join(__dir__, "templates", "assets")
|
|
6
|
+
|
|
7
|
+
CONTENT_TYPES = {
|
|
8
|
+
".css" => "text/css; charset=utf-8",
|
|
9
|
+
".js" => "application/javascript; charset=utf-8",
|
|
10
|
+
".png" => "image/png",
|
|
11
|
+
".jpg" => "image/jpeg",
|
|
12
|
+
".jpeg" => "image/jpeg",
|
|
13
|
+
".svg" => "image/svg+xml",
|
|
14
|
+
".woff" => "font/woff2",
|
|
15
|
+
".woff2" => "font/woff2",
|
|
16
|
+
".ico" => "image/x-icon"
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
def serve(request_path)
|
|
20
|
+
asset_path = extract_asset_path(request_path)
|
|
21
|
+
|
|
22
|
+
return forbidden_response if directory_traversal?(asset_path)
|
|
23
|
+
|
|
24
|
+
file_path = build_file_path(asset_path)
|
|
25
|
+
return not_found_response unless File.file?(file_path)
|
|
26
|
+
|
|
27
|
+
serve_file(file_path)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def extract_asset_path(request_path)
|
|
33
|
+
request_path.delete_prefix("/assets/")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def directory_traversal?(path)
|
|
37
|
+
path.include?("..")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def build_file_path(asset_path)
|
|
41
|
+
File.join(ASSETS_PATH, asset_path)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def serve_file(file_path)
|
|
45
|
+
content = File.read(file_path)
|
|
46
|
+
content_type = detect_content_type(file_path)
|
|
47
|
+
|
|
48
|
+
[200, { "Content-Type" => content_type }, [content]]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def detect_content_type(file_path)
|
|
52
|
+
extension = File.extname(file_path)
|
|
53
|
+
CONTENT_TYPES.fetch(extension, "application/octet-stream")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def forbidden_response
|
|
57
|
+
[403, { "Content-Type" => "text/plain" }, ["403 Forbidden"]]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def not_found_response
|
|
61
|
+
[404, { "Content-Type" => "text/plain" }, ["404 Not Found"]]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/docyard/cli.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module Docyard
|
|
6
|
+
class CLI < Thor
|
|
7
|
+
def self.exit_on_failure?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc "version", "Show docyard version"
|
|
12
|
+
def version
|
|
13
|
+
puts "docyard #{Docyard::VERSION}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "init", "Initialize a new docyard project"
|
|
17
|
+
def init
|
|
18
|
+
initializer = Docyard::Initializer.new
|
|
19
|
+
exit(1) unless initializer.run
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "serve", "Start the development server"
|
|
23
|
+
method_option :port, type: :numeric, default: 4200, aliases: "-p", desc: "Port to run the server on"
|
|
24
|
+
method_option :host, type: :string, default: "localhost", aliases: "-h", desc: "Host to bind the server to"
|
|
25
|
+
def serve
|
|
26
|
+
require_relative "server"
|
|
27
|
+
server = Docyard::Server.new(
|
|
28
|
+
port: options[:port],
|
|
29
|
+
host: options[:host]
|
|
30
|
+
)
|
|
31
|
+
server.start
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docyard
|
|
4
|
+
module Constants
|
|
5
|
+
CONTENT_TYPE_HTML = "text/html; charset=utf-8"
|
|
6
|
+
CONTENT_TYPE_JSON = "application/json; charset=utf-8"
|
|
7
|
+
CONTENT_TYPE_CSS = "text/css; charset=utf-8"
|
|
8
|
+
CONTENT_TYPE_JS = "application/javascript; charset=utf-8"
|
|
9
|
+
|
|
10
|
+
RELOAD_ENDPOINT = "/_docyard/reload"
|
|
11
|
+
ASSETS_PREFIX = "/assets/"
|
|
12
|
+
|
|
13
|
+
INDEX_FILE = "index"
|
|
14
|
+
INDEX_TITLE = "Home"
|
|
15
|
+
|
|
16
|
+
MARKDOWN_EXTENSION = ".md"
|
|
17
|
+
HTML_EXTENSION = ".html"
|
|
18
|
+
|
|
19
|
+
STATUS_OK = 200
|
|
20
|
+
STATUS_NOT_FOUND = 404
|
|
21
|
+
STATUS_INTERNAL_ERROR = 500
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docyard
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
|
|
6
|
+
class FileNotFoundError < Error
|
|
7
|
+
attr_reader :path
|
|
8
|
+
|
|
9
|
+
def initialize(path)
|
|
10
|
+
@path = path
|
|
11
|
+
super("File not found: #{path}")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class InvalidPathError < Error; end
|
|
16
|
+
|
|
17
|
+
class MarkdownParseError < Error
|
|
18
|
+
attr_reader :file_path, :original_error
|
|
19
|
+
|
|
20
|
+
def initialize(file_path, original_error)
|
|
21
|
+
@file_path = file_path
|
|
22
|
+
@original_error = original_error
|
|
23
|
+
super("Failed to parse markdown file #{file_path}: #{original_error.message}")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class TemplateRenderError < Error
|
|
28
|
+
attr_reader :template_path, :original_error
|
|
29
|
+
|
|
30
|
+
def initialize(template_path, original_error)
|
|
31
|
+
@template_path = template_path
|
|
32
|
+
@original_error = original_error
|
|
33
|
+
super("Failed to render template #{template_path}: #{original_error.message}")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class ReloadCheckError < Error
|
|
38
|
+
attr_reader :original_error
|
|
39
|
+
|
|
40
|
+
def initialize(original_error)
|
|
41
|
+
@original_error = original_error
|
|
42
|
+
super("Reload check failed: #{original_error.message}")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class AssetNotFoundError < Error
|
|
47
|
+
attr_reader :asset_path
|
|
48
|
+
|
|
49
|
+
def initialize(asset_path)
|
|
50
|
+
@asset_path = asset_path
|
|
51
|
+
super("Asset not found: #{asset_path}")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "listen"
|
|
4
|
+
|
|
5
|
+
module Docyard
|
|
6
|
+
class FileWatcher
|
|
7
|
+
attr_reader :last_modified_time
|
|
8
|
+
|
|
9
|
+
def initialize(docs_path)
|
|
10
|
+
@docs_path = docs_path
|
|
11
|
+
@last_modified_time = Time.now
|
|
12
|
+
@listener = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def start
|
|
16
|
+
@listener = Listen.to(@docs_path, only: /\.md$/) do |modified, added, removed|
|
|
17
|
+
handle_changes(modified, added, removed)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@listener.start
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def stop
|
|
24
|
+
@listener&.stop
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
Docyard.logger.error "Error stopping file watcher: #{e.class} - #{e.message}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def changed_since?(timestamp)
|
|
30
|
+
@last_modified_time > timestamp
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def handle_changes(modified, added, removed)
|
|
36
|
+
return if modified.empty? && added.empty? && removed.empty?
|
|
37
|
+
|
|
38
|
+
@last_modified_time = Time.now
|
|
39
|
+
Docyard.logger.info "Files changed, triggering reload..."
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|