diff --git a/.editorconfig b/.editorconfig index 0de50015..42588240 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,7 @@ indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true +quote_type = single [*.md] trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore index f71b8764..a6082fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,113 +1,14 @@ -### OSX ### -# General .DS_Store -.AppleDouble -.LSOverride -# Thumbnails -._* +.vitepress/dist +.vitepress/cache -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent +old/ -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo +vendor/ -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# pnpm link folder -pnpm-global - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache +logs/ +*.log -# rollup.js default build output dist/ - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# Temporary folders -tmp/ -temp/ -TODOs.md -src/api/index.json -src/examples/data.json -src/tutorial/data.json -draft.md - -.idea/ diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 2ecc4f49..00000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "semi": false, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 75 -} diff --git a/.vitepress/config.mts b/.vitepress/config.mts new file mode 100644 index 00000000..608b7868 --- /dev/null +++ b/.vitepress/config.mts @@ -0,0 +1,85 @@ +import { defineConfig } from 'vitepress'; +import { + groupIconMdPlugin, + groupIconVitePlugin, +} from 'vitepress-plugin-group-icons'; + +import head from './config/head'; +import nav from './config/navbar'; +import sidebar from './config/sidebar'; + +export default defineConfig({ + head, + lang: 'en-US', + srcDir: 'src', + scrollOffset: 'header', + srcExclude: ['tutorial/**/description.md'], + + title: 'Leaf PHP', + description: 'Simple and elegant PHP', + + themeConfig: { + nav, + sidebar, + + logo: '/logo-circle.png', + siteTitle: 'Leaf PHP', + + search: { + provider: 'local', + // provider: 'algolia', + // options: { + // appId: 'Q38TT8XUN9', + // indexName: 'leafphp', + // apiKey: '87b4b8d90960f7a326dfd4c8781a5a74', + // }, + }, + + socialLinks: [ + // { icon: 'languages', link: '/translations/' }, + { icon: 'github', link: 'https://github.com/vuejs/vitepress' }, + { icon: 'twitter', link: 'https://x.com/leafphp' }, + { icon: 'discord', link: 'https://discord.gg/Pkrm9NJPE3' }, + // { + // icon: 'youtube', + // link: 'https://www.youtube.com/channel/UCllE-GsYy10RkxBUK0HIffw' + // } + ], + + lastUpdated: { + text: 'Updated at', + formatOptions: { + dateStyle: 'full', + timeStyle: 'medium', + }, + }, + + editLink: { + text: 'Edit this page on GitHub', + // pattern: 'https://github.com/leafsphp/docs/edit/next/src/:path', + pattern: ({ filePath }) => { + if (filePath.startsWith('packages/')) { + return `https://github.com/leafsphp/docs/edit/next/src/${filePath}`; + } else { + return `https://github.com/leafsphp/docs/edit/next/src/${filePath}`; + } + }, + }, + + footer: { + message: `Released under the MIT License.`, + copyright: `Copyright © 2019-${new Date().getFullYear()} Michael Darko-Duodu`, + }, + }, + + markdown: { + lineNumbers: true, + config(md) { + md.use(groupIconMdPlugin); + }, + }, + + vite: { + plugins: [groupIconVitePlugin()], + }, +}); diff --git a/.vitepress/config.ts b/.vitepress/config.ts deleted file mode 100644 index e9db8133..00000000 --- a/.vitepress/config.ts +++ /dev/null @@ -1,703 +0,0 @@ -import fs from 'fs' -import path from 'path' -import { defineConfigWithTheme } from 'vitepress' -import type { Config as ThemeConfig } from '@leafphp/docs-theme' -import baseConfig from '@leafphp/docs-theme/config' -import { headerPlugin } from './headerMdPlugin' - -const nav = [ - { - text: 'Docs', - activeMatch: `^/(docs|style-guide|examples|tutorial)/`, - items: [ - { text: 'Guide', link: '/docs/introduction/' }, - { text: 'Tutorial', link: '/tutorial/' }, - { text: 'Online Playground', link: 'https://sandbox.leafphp.dev/' }, - { - text: 'Leaf Modules', - link: '/modules/' - }, - { - text: 'Migration from Leaf 2', - link: '/docs/migration/introduction' - }, - { - text: 'Leaf 2 Docs', - link: 'https://archive.leafphp.dev' - }, - { - text: 'Changelog', - link: '/changes' - } - ] - }, - { - text: 'Ecosystem', - activeMatch: `^/ecosystem/`, - items: [ - { - text: 'Core Projects', - items: [ - { - text: 'Leaf MVC', - link: 'https://mvc.leafphp.dev/' - }, - { - text: 'Leaf API', - link: 'https://api.leafphp.dev/' - }, - { - text: 'Leaf Skeleton', - link: 'https://skeleton.leafphp.dev/' - } - ] - }, - { - text: 'Tooling', - ariaLabel: 'Tooling Menu', - items: [ - { - text: 'Aloe CLI', - link: '/aloe-cli/' - }, - { - text: 'Leaf CLI', - link: '/docs/cli/' - }, - { - text: 'Leaf UI', - link: 'https://ui.leafphp.dev/' - } - ] - }, - { - text: 'Resources', - ariaLabel: 'Resources Menu', - items: [ - { - text: 'Project Showcase', - link: '/ecosystem/showcase' - }, - { - text: 'Codelabs', - link: '/codelabs/' - } - ] - } - ] - }, - { - text: 'Community', - activeMatch: `^/(about|community)/`, - items: [ - { - text: 'Community', - ariaLabel: 'Community Menu', - items: [ - { - text: 'Contribute to Leaf', - link: '/community/contributing/' - }, - { - text: 'Contribute to docs', - link: '/community/contributing/writing-guide' - }, - { - text: 'Blog', - link: 'https://blog.leafphp.dev' - }, - { - text: 'Events', - link: '/events/' - }, - { - text: 'Team', - link: '/community/team' - }, - { - text: 'Join', - link: '/community/join' - }, - { - text: 'FAQ', - link: '/community/faq' - } - ] - }, - { - text: 'Help', - ariaLabel: 'Help Menu', - items: [ - { - text: 'Leaf Forum', - link: 'https://github.com/leafsphp/leaf/discussions/37' - }, - { - text: 'YouTube', - link: 'https://www.youtube.com/channel/UCllE-GsYy10RkxBUK0HIffw' - }, - { - text: 'Discord', - link: 'https://discord.gg/Pkrm9NJPE3' - }, - { - text: 'GitHub', - link: 'https://github.com/leafsphp/leaf' - } - ] - } - ] - }, - { - text: 'Support Leaf', - link: '/support/' - } - // { - // text: 'Partners', - // link: '/partners/', - // activeMatch: `^/partners/` - // } -] - -const mainSidebar = [ - { - text: 'Quick Start', - collapsible: true, - collapsed: true, - items: [ - { text: 'Introduction', link: '/docs/introduction/' }, - { text: 'Why Leaf?', link: '/docs/introduction/why' }, - { text: 'Installation', link: '/docs/introduction/installation' }, - { text: 'Leaf + MVC', link: '/docs/mvc/' }, - { text: 'Using Docker', link: '/docs/introduction/docker' }, - { text: 'Migration Guide', link: '/docs/migration/other' }, - ] - }, - { - text: 'Introduction', - collapsible: true, - collapsed: true, - items: [ - { text: 'Leaf CLI', link: '/docs/cli/' }, - { text: 'Modules', link: '/modules/' }, - { text: 'Functional Mode', link: '/docs/tooling/functions' }, - { text: 'Leaf tutorial', link: '/tutorial/' }, - { text: 'Your first app', link: '/docs/introduction/first-app' }, - { text: 'Leaf Devtools', link: '/modules/devtools/' }, - { text: 'Testing', link: '/docs/tooling/testing' }, - { text: 'Deployment', link: '/docs/tooling/deployment' } - ] - }, - { - text: 'Config', - collapsible: true, - collapsed: true, - items: [ - { text: 'Overview', link: '/docs/config/' }, - { text: 'App settings', link: '/docs/config/settings' }, - { text: 'Application Env', link: '/docs/config/nsm' }, - { text: 'URL Rewriting', link: '/docs/introduction/url-rewriting' }, - { text: 'Dependency Injection', link: '/docs/tooling/container' }, - ] - }, - { - text: 'The basics', - collapsible: true, - collapsed: true, - items: [ - { text: 'Request', link: '/modules/http/v/2/request' }, - { text: 'Response', link: '/modules/http/v/2/response' }, - { text: 'Headers', link: '/modules/http/v/2/headers' }, - { text: 'CORS', link: '/modules/cors/' }, - { text: 'Session', link: '/modules/session/' }, - { text: 'Session Flash', link: '/modules/session/flash' }, - { text: 'Cookies', link: '/modules/cookies/' }, - { text: 'Leaf View', link: '/docs/tooling/view' }, - { text: 'Middleware', link: '/docs/routing/middleware' }, - { text: 'Error Handling', link: '/docs/routing/errors' } - ] - }, - { - text: 'Routing', - collapsible: true, - collapsed: true, - items: [ - { text: 'Basic Routing', link: '/docs/routing/' }, - { text: 'MVC Support', link: '/docs/routing/mvc' }, - { text: 'Route Groups', link: '/docs/routing/sub-routing' }, - { text: 'Dynamic routing', link: '/docs/routing/dynamic' }, - { - text: 'Optional Route sub-patterns', - link: '/docs/routing/sub-patterns' - }, - { text: 'Subfolder support', link: '/docs/routing/sub-folder' }, - { text: 'Using controllers', link: '/docs/routing/controller' } - ] - }, - { - text: 'Database', - collapsible: true, - collapsed: true, - items: [ - { text: 'Introduction', link: '/modules/db/' }, - { text: 'MVC Support', link: '/modules/db/mvc' }, - { text: 'Query Builder', link: '/modules/db/v/2/builder' }, - { text: 'Leaf Redis', link: '/modules/redis/' } - ] - }, - { - text: 'Authentication', - collapsible: true, - collapsed: true, - items: [ - { text: 'Introduction', link: '/modules/auth/' }, - { text: 'MVC Support', link: '/modules/auth/mvc' }, - { text: 'Auth Config', link: '/modules/auth/config' }, - { text: 'User Login', link: '/modules/auth/login' }, - { text: 'User Sign Up', link: '/modules/auth/signup' }, - { text: 'Auth Session', link: '/modules/auth/session' }, - { - text: 'Protecting your Routes', - link: '/modules/auth/protecting-your-routes' - }, - { text: 'Updating logged-in user', link: '/modules/auth/update' }, - { text: 'Helper methods', link: '/modules/auth/helpers' } - ] - }, - { - text: 'Utilities', - collapsible: true, - collapsed: true, - items: [ - { text: 'Validation', link: '/modules/forms/v/2/' }, - { text: 'Leaf Password', link: '/modules/password/' }, - { text: 'Leaf Anchor', link: '/modules/anchor/' }, - { text: 'Date/Time', link: '/modules/date/' }, - { text: 'Data Fetching', link: '/modules/fetch/' }, - { text: 'Logging', link: '/docs/tooling/logging' } - ] - }, - { - text: 'Digging Deeper', - collapsible: true, - collapsed: true, - items: [ - { text: 'CSRF', link: '/modules/anchor/csrf/' }, - { text: 'HTTP Cache', link: '/modules/http/v/2/cache' }, - { text: 'Eien Server', link: '/modules/eien/' }, - { text: 'Leaf Mail', link: '/modules/mail/' }, - { text: 'File System', link: '/modules/fs/' }, - { text: 'Queues/Jobs', link: '/modules/queues/' }, - ] - }, - { - text: 'Frontend', - collapsible: true, - collapsed: true, - items: [ - { text: 'Introduction', link: '/modules/views/' }, - { text: 'Bare UI', link: '/modules/views/bareui/' }, - { text: 'Leaf Blade', link: '/modules/views/blade/' }, - { text: 'Leaf Veins', link: '/modules/views/veins/' }, - { text: 'Other Engines', link: '/modules/views/third-party/' }, - { text: 'Vite JS', link: '/modules/views/vite/' }, - { text: 'Inertia JS', link: '/modules/views/inertia/' }, - { text: 'Viewi PHP', link: '/modules/views/viewi/' }, - { text: 'Leaf UI', link: '/modules/views/leaf-ui/' }, - ] - }, - { - text: 'MVC Integrations', - collapsible: true, - collapsed: true, - items: [ - { text: 'Leaf MVC', link: '/docs/leafmvc/' }, - { text: 'Leaf API', link: '/docs/leafapi/' }, - { text: 'Skeleton', link: '/docs/skeleton/' }, - { text: 'MVC Config', link: '/docs/mvc/config' }, - { text: 'Controllers', link: '/docs/mvc/controllers' }, - { text: 'Views', link: '/docs/mvc/views' }, - { text: 'Models', link: '/docs/mvc/models' }, - { text: 'Migrations', link: '/docs/mvc/migrations' }, - { text: 'Schema', link: '/docs/mvc/schema' }, - { text: 'Seeders', link: '/docs/mvc/seeds' }, - { text: 'Factories', link: '/modules/mvc-core/factories' }, - { text: 'Writing Commands', link: '/docs/mvc/commands' }, - { text: 'Mailing', link: '/docs/mvc/mail' }, - { text: 'MVC Helpers', link: '/docs/mvc/globals' }, - { text: 'Custom Libraries', link: '/docs/mvc/libraries' }, - { text: 'MVC Console Tool', link: '/docs/mvc/console' }, - ] - }, - { - text: 'Codelabs', - collapsible: true, - collapsed: true, - items: [ - { text: 'Intro', link: '/codelabs/' }, - { text: 'Contributing', link: '/codelabs/contributing' }, - { text: 'Deployment', link: '/codelabs/experiments/deployment/' } - // { text: 'Authentication', link: '/codelabs/experiments/auth/' }, - ] - } - // { - // text: 'Archive', - // collapsible: true, - // collapsed: true, - // items: [ - // { text: 'Leaf Http', link: '/modules/http/' }, - // { text: 'Leaf Http v1', link: '/modules/http/v/1/' } - // ] - // } -] - -const communitySidebar = [ - { - text: 'Community', - collapsible: true, - items: [ - { text: 'History', link: '/community/history' }, - { text: 'FAQ', link: '/community/faq' }, - { text: 'Blog', link: 'https://blog.leafphp.dev' }, - { text: 'Meet the Team', link: '/community/team' }, - { text: 'Our Community', link: '/community/join' }, - { text: 'Code of Conduct', link: '/coc/' }, - { text: 'Contribution Guide', link: '/community/contributing/' }, - { - text: 'Writing Guide', - link: '/community/contributing/writing-guide' - }, - { text: 'Support Leaf', link: '/support/' }, - { text: 'Twitter', link: 'https://twitter.com/leafphp' }, - { text: 'Discord', link: 'https://discord.gg/Pkrm9NJPE3' }, - { - text: 'YouTube', - link: 'https://www.youtube.com/channel/UCllE-GsYy10RkxBUK0HIffw' - } - ] - } -] - -export const sidebar = { - '/docs/': mainSidebar, - '/modules/': mainSidebar, - '/codelabs/': mainSidebar, - '/aloe-cli/': [ - { - text: 'Aloe CLI', - collapsible: true, - items: [ - { text: 'Home', link: '/aloe-cli/' }, - { - text: 'Getting Started', - link: '/aloe-cli/v/1.2.3/getting-started/' - } - ] - }, - { - text: 'Default Commands', - collapsible: true, - items: [ - { - text: 'Misc Commands', - link: '/aloe-cli/v/1.2.3/commands/misc-commands' - }, - { - text: '"Generate" Commands', - link: '/aloe-cli/v/1.2.3/commands/g-commands' - }, - { - text: '"Delete" Commands', - link: '/aloe-cli/v/1.2.3/commands/d-commands' - }, - { - text: '"DB" Commands', - link: '/aloe-cli/v/1.2.3/commands/db-commands' - } - ] - }, - { - text: 'Aloe CLI', - collapsible: true, - items: [ - { - text: 'Custom Commands', - link: '/aloe-cli/v/1.2.3/commands/custom' - }, - { - text: 'Command IO', - link: '/aloe-cli/v/1.2.3/commands/io' - } - ] - }, - { - text: 'Aloe Misc', - collapsible: true, - items: [ - { text: 'Aloe Libraries', link: '/aloe-cli/v/1.2.3/libraries' }, - { text: 'Aloe Installer', link: '/aloe-cli/v/1.2.3/installer' } - ] - } - ], - '/community/': communitySidebar, - '/coc/': communitySidebar, - '/style-guide/': [ - { - text: 'Style Guide', - items: [ - { - text: 'Overview', - link: '/style-guide/' - }, - { - text: 'A - Essential', - link: '/style-guide/rules-essential' - }, - { - text: 'B - Strongly Recommended', - link: '/style-guide/rules-strongly-recommended' - }, - { - text: 'C - Recommended', - link: '/style-guide/rules-recommended' - }, - { - text: 'D - Use with Caution', - link: '/style-guide/rules-use-with-caution' - } - ] - } - ] -} - -export default defineConfigWithTheme({ - logo: '/logo-circle.png', - extends: baseConfig, - lang: 'en-US', - title: 'Leaf PHP', - description: 'Leaf PHP - Simple and elegant PHP', - srcDir: 'src', - srcExclude: ['tutorial/**/description.md'], - scrollOffset: 'header', - - head: [ - ['meta', { name: 'twitter:site', content: '@leafphp' }], - ['meta', { name: 'twitter:card', content: 'summary' }], - [ - 'meta', - { - name: 'twitter:image', - content: - 'https://repository-images.githubusercontent.com/214705101/0ff19323-d2c5-46f5-a582-0b1f3a6eabcc' - } - ], - [ - 'meta', - { - name: 'og:image', - content: - 'https://repository-images.githubusercontent.com/214705101/0ff19323-d2c5-46f5-a582-0b1f3a6eabcc' - } - ], - [ - 'link', - { - rel: 'preload', - as: 'style', - href: '/global.css' - } - ], - [ - 'link', - { - rel: 'stylesheet', - href: '/global.css' - } - ], - [ - 'link', - { - rel: 'preload', - as: 'style', - href: '/449.css' - } - ], - [ - 'link', - { - rel: 'stylesheet', - href: '/449.css' - } - ], - [ - 'link', - { - rel: 'icon', - href: '/logo-circle.png' - } - ], - ['link', { rel: 'manifest', href: '/manifest.json' }], - [ - 'meta', - { - name: 'background_color', - content: '#001e26' - } - ], - [ - 'meta', - { - name: 'theme-color', - content: '#001e26' - } - ], - ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], - [ - 'meta', - { - name: 'viewport', - content: 'width=device-width, initial-scale=1.0' - } - ], - [ - 'meta', - { name: 'apple-mobile-web-app-status-bar-style', content: 'black' } - ], - [ - 'link', - { - rel: 'apple-touch-icon', - href: '/images/icons/apple-icon-152x152.png' - } - ], - [ - 'meta', - { - name: 'msapplication-TileImage', - content: '/images/icons/ms-icon-144x144.png' - } - ], - ['meta', { name: 'msapplication-TileColor', content: '#000000' }], - [ - 'link', - { - href: 'https://fonts.googleapis.com/icon?family=Material+Icons', - rel: 'stylesheet' - } - ], - [ - 'link', - { - href: 'https://fonts.googleapis.com/css?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500|DM+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700|Inter:300,400,500,600|Open+Sans:400,600;display=swap', - rel: 'stylesheet' - } - ], - [ - 'script', - {}, - fs.readFileSync( - path.resolve(__dirname, './inlined-scripts/restorePreference.js'), - 'utf-8' - ) - ], - [ - 'script', - { - async: '', - src: 'https://www.googletagmanager.com/gtag/js?id=G-QGZVHHLK12' - } - ], - [ - 'script', - {}, - ` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - - gtag('config', 'G-QGZVHHLK12'); - ` - ], - [ - 'script', - { - src: 'https://cdn.usefathom.com/script.js', - 'data-site': 'XNOLWPLB', - 'data-spa': 'auto', - defer: '' - } - ] - ], - - themeConfig: { - nav, - sidebar, - algolia: { - appId: 'Q38TT8XUN9', - indexName: 'leafphp', - apiKey: '87b4b8d90960f7a326dfd4c8781a5a74' - }, - - socialLinks: [ - { icon: 'languages', link: '/translations/' }, - { icon: 'github', link: 'https://github.com/leafsphp/leaf' }, - { icon: 'twitter', link: 'https://twitter.com/leafphp' }, - { icon: 'discord', link: 'https://discord.gg/Pkrm9NJPE3' } - // { - // icon: 'youtube', - // link: 'https://www.youtube.com/channel/UCllE-GsYy10RkxBUK0HIffw' - // } - ], - - editLink: { - repo: 'leafsphp/docs#master', - text: 'Edit this page on GitHub' - }, - - footer: { - license: { - text: 'MIT License', - link: 'https://github.com/leafsphp/leaf/blob/v3.x/LICENSE' - }, - copyright: `Copyright © 2019-${new Date().getFullYear()} Michael Darko-Duodu` - } - }, - - markdown: { - config(md) { - md.use(headerPlugin) - } - }, - - vite: { - define: { - __VUE_OPTIONS_API__: false - }, - optimizeDeps: { - include: ['gsap', 'dynamics.js'], - exclude: ['@vue/repl', '@leafphp/docs-theme'] - }, - // @ts-ignore - ssr: { - external: ['@vue/repl'] - }, - server: { - host: true, - fs: { - // for when developing with locally linked theme - allow: ['../..'] - } - }, - build: { - minify: 'terser', - chunkSizeWarningLimit: Infinity - }, - json: { - stringify: true - } - }, - - vue: { - reactivityTransform: true - } -}) diff --git a/.vitepress/config/head.ts b/.vitepress/config/head.ts new file mode 100644 index 00000000..b23d2265 --- /dev/null +++ b/.vitepress/config/head.ts @@ -0,0 +1,170 @@ +import { HeadConfig } from 'vitepress'; + +const head: HeadConfig[] = [ + [ + 'meta', + { + name: 'robots', + content: 'index, follow', + }, + ], + [ + 'meta', + { + name: 'description', + content: + 'Leaf is a lightweight and user-friendly framework designed for quick and efficient development. It features a zero-config setup and an ecosystem of tools, making it ideal for building scalable apps with ease.', + }, + ], + ['meta', { name: 'twitter:site', content: '@leafphp' }], + ['meta', { name: 'twitter:url', content: 'https://leafphp.dev' }], + ['meta', { name: 'twitter:card', content: 'summary_large_image' }], + [ + 'meta', + { + name: 'twitter:title', + content: 'Leaf PHP - Elegant PHP for Modern Development', + }, + ], + [ + 'meta', + { + name: 'twitter:description', + content: + 'Leaf is a lightweight and user-friendly framework designed for quick and efficient development. It features a zero-config setup and an ecosystem of tools, making it ideal for building scalable apps with ease.', + }, + ], + [ + 'meta', + { + name: 'twitter:image', + content: + 'https://repository-images.githubusercontent.com/214705101/0ff19323-d2c5-46f5-a582-0b1f3a6eabcc', + }, + ], + [ + 'meta', + { + name: 'og:title', + content: 'Leaf PHP - Elegant PHP for Modern Development', + }, + ], + [ + 'meta', + { + name: 'og:type', + content: 'website', + }, + ], + [ + 'meta', + { + name: 'og:url', + content: 'https://leafphp.dev', + }, + ], + [ + 'meta', + { + name: 'og:image', + content: + 'https://repository-images.githubusercontent.com/214705101/0ff19323-d2c5-46f5-a582-0b1f3a6eabcc', + }, + ], + [ + 'meta', + { + name: 'og:description', + content: + 'Leaf is a lightweight and user-friendly framework designed for quick and efficient development. It features a zero-config setup and an ecosystem of tools, making it ideal for building scalable apps with ease.', + }, + ], + [ + 'link', + { + rel: 'icon', + href: '/logo-circle.png', + }, + ], + ['link', { rel: 'manifest', href: '/manifest.json' }], + [ + 'meta', + { + name: 'background_color', + content: '#001e26', + }, + ], + [ + 'meta', + { + name: 'theme-color', + content: '#001e26', + }, + ], + ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], + [ + 'meta', + { + name: 'viewport', + content: 'width=device-width, initial-scale=1.0', + }, + ], + ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], + [ + 'link', + { + rel: 'apple-touch-icon', + href: '/images/icons/apple-icon-152x152.png', + }, + ], + [ + 'meta', + { + name: 'msapplication-TileImage', + content: '/images/icons/ms-icon-144x144.png', + }, + ], + ['meta', { name: 'msapplication-TileColor', content: '#000000' }], + [ + 'link', + { + href: 'https://fonts.googleapis.com/css?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500|DM+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700|Inter:300,400,500,600|Open+Sans:400,600;display=swap', + rel: 'stylesheet', + }, + ], + [ + 'script', + { + async: '', + src: 'https://static.elfsight.com/platform/platform.js', + }, + ], + // [ + // 'script', + // {}, + // fs.readFileSync( + // path.resolve(__dirname, './inlined-scripts/restorePreference.js'), + // 'utf-8' + // ), + // ], + // [ + // 'script', + // { + // async: '', + // src: 'https://www.googletagmanager.com/gtag/js?id=G-QGZVHHLK12', + // }, + // ], + // [ + // 'script', + // {}, + // ` + // window.dataLayer = window.dataLayer || []; + // function gtag(){dataLayer.push(arguments);} + // gtag('js', new Date()); + + // gtag('config', 'G-QGZVHHLK12'); + // `, + // ], +]; + +export default head; diff --git a/.vitepress/config/navbar.ts b/.vitepress/config/navbar.ts new file mode 100644 index 00000000..b0ef284d --- /dev/null +++ b/.vitepress/config/navbar.ts @@ -0,0 +1,137 @@ +import { DefaultTheme } from 'vitepress'; + +const nav: DefaultTheme.NavItem[] = [ + { + text: 'Docs', + activeMatch: `^/(docs|examples)/`, + items: [ + { text: 'Guide', link: '/docs/' }, + { text: 'Tutorial', link: '/tutorial/' }, + { + text: 'Leaf + MVC', + link: '/docs/mvc/', + }, + { + text: 'Leaf Modules', + link: '/docs/modules', + }, + { + text: 'Leaf CLI', + link: '/docs/cli/', + }, + { + text: 'MVC Console', + link: '/docs/mvc/console/', + }, + ], + }, + + { + text: 'Ecosystem', + activeMatch: `^/ecosystem/`, + items: [ + { + text: 'Resources', + items: [ + { text: 'Online Playground', link: 'https://sandbox.leafphp.dev/' }, + { + text: 'Codelabs', + link: '/codelabs/', + }, + // { + // text: 'Leaf UI', + // link: 'https://ui.leafphp.dev/', + // }, + ], + }, + { + text: 'Other', + items: [ + { + text: 'Hana JS', + link: 'https://hanajs.dev', + }, + { + text: 'Naytive', + link: 'https://naytive.netlify.app', + }, + ], + }, + { + text: 'Help', + items: [ + // { + // text: 'Leaf Forum', + // link: 'https://github.com/leafsphp/leaf/discussions/37', + // }, + { + text: 'YouTube', + link: 'https://www.youtube.com/channel/UCllE-GsYy10RkxBUK0HIffw', + }, + { + text: 'Discord', + link: 'https://discord.gg/Pkrm9NJPE3', + }, + { + text: 'GitHub', + link: 'https://github.com/leafsphp/leaf', + }, + ], + }, + ], + }, + + { + text: 'Community', + activeMatch: `^/(about|community)/`, + items: [ + { + text: 'Community', + items: [ + { + text: 'Leaf Community', + link: '/community/', + }, + { + text: 'Contribute to Leaf', + link: '/community/contribute', + }, + { + text: 'Community Guides', + link: '/community/guide', + }, + { + text: 'Contribute to docs', + link: '/community/docs-writing-guide', + }, + { + text: 'Changelog', + link: '/community/releases', + }, + // { + // text: 'Project Showcase', + // link: '/community/showcase', + // }, + { + text: 'Blog', + link: 'https://blog.leafphp.dev', + }, + { + text: 'Team', + link: '/community/team', + }, + { + text: 'FAQ', + link: '/community/faq', + }, + ], + }, + ], + }, + { + text: 'Support Leaf', + link: '/support/', + }, +]; + +export default nav; diff --git a/.vitepress/config/sidebar.ts b/.vitepress/config/sidebar.ts new file mode 100644 index 00000000..2b77a582 --- /dev/null +++ b/.vitepress/config/sidebar.ts @@ -0,0 +1,181 @@ +const sidebar = [ + { + text: 'Quick Start', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Introduction', link: '/docs/' }, + { text: 'Installation', link: '/docs/installation' }, + { text: 'Migration Guide', link: '/docs/migrating' }, + // { text: 'Functional Mode', link: '/docs/config/functional-mode' }, + { text: 'Modules', link: '/docs/modules' }, + ], + }, + { + text: 'Leaf CLI', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'CLI Intro', link: '/docs/cli/' }, + { text: 'Creating apps', link: '/docs/cli/creating-an-app' }, + { text: 'Managing apps', link: '/docs/cli/managing-apps' }, + ], + }, + { + text: 'Routing', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Basic Routing', link: '/docs/routing/' }, + { text: 'Route Groups', link: '/docs/routing/route-groups' }, + { text: 'Dynamic routing', link: '/docs/routing/dynamic' }, + { text: 'Middleware', link: '/docs/routing/middleware/' }, + { text: 'Middleware in Leaf MVC', link: '/docs/routing/middleware/mvc' }, + ], + }, + { + text: 'Request/Response', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Request', link: '/docs/http/request' }, + { text: 'Response', link: '/docs/http/response' }, + { text: 'Headers', link: '/docs/http/headers' }, + { text: 'CORS', link: '/docs/http/cors' }, + ], + }, + { + text: 'Config & Deployment', + // collapsible: true, + // collapsed: true, + items: [ + // { text: 'Overview', link: '/docs/config/' }, + // { text: 'App settings', link: '/docs/config/settings' }, + { text: 'Application Env', link: '/docs/config/environment' }, + { text: 'URL Rewriting', link: '/docs/routing/url-rewriting' }, + { text: 'Error Handling', link: '/docs/routing/error-handling' }, + { text: 'Dependency Injection', link: '/docs/config/container' }, + // { text: 'Logging', link: '/docs/utils/logging' }, + { text: 'Using Swoole', link: '/docs/swoole' }, + { text: 'Using Docker', link: '/docs/docker' }, + { text: 'Debugging', link: '/docs/config/debugging' }, + // { text: 'Leaf Devtools', link: '/docs/utils/devtools' }, + // { text: 'Deployment', link: '/docs/config/deployment' }, + { text: 'Testing/Linting', link: '/docs/utils/testing' }, + ], + }, + { + text: 'Database', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Introduction', link: '/docs/database/' }, + // { text: 'MVC Support', link: '/docs/database/mvc' }, + { text: 'Query Builder', link: '/docs/database/builder' }, + { text: 'Leaf Redis', link: '/docs/database/redis' }, + // { text: 'Other DB Engines', link: '/docs/database/other' }, + ], + }, + { + text: 'Authentication', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Introduction', link: '/docs/auth/' }, + // { text: 'MVC Support', link: '/docs/auth/mvc' }, + // { text: 'Auth Config', link: '/docs/auth/config' }, + { text: 'User Login', link: '/docs/auth/login' }, + { text: 'User Sign Up', link: '/docs/auth/signup' }, + { text: 'Auth User', link: '/docs/auth/user' }, + { + text: 'Protecting your routes', + link: '/docs/auth/protected-routes', + }, + // { text: 'Updating logged-in user', link: '/docs/auth/update' }, + // { text: 'Build your own auth library', link: '/docs/auth/helpers' }, + ], + }, + { + text: 'Sessions', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Using Sessions', link: '/docs/http/session' }, + { text: 'Session Flash', link: '/docs/http/flash' }, + { text: 'Cookies', link: '/docs/http/cookies' }, + ], + }, + { + text: 'Security', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Validation', link: '/docs/data/validation' }, + { text: 'CSRF Protection', link: '/docs/security/csrf' }, + { text: 'Leaf Password', link: '/docs/data/encryption' }, + { text: 'General Security', link: '/docs/security/anchor' }, + ], + }, + { + text: 'Utilities', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Date/Time', link: '/docs/utils/date' }, + { text: 'Data Fetching', link: '/docs/utils/fetch' }, + { text: 'HTTP Cache', link: '/docs/http/caching' }, + { text: 'Leaf Mail', link: '/docs/utils/mail/' }, + { text: 'File System', link: '/docs/utils/fs' }, + // { text: 'Queues/Jobs', link: '/docs/utils/queues' }, + ], + }, + { + text: 'Frontend', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Introduction', link: '/docs/frontend/' }, + { text: 'Bare UI', link: '/docs/frontend/bareui' }, + { text: 'Leaf Blade', link: '/docs/frontend/blade' }, + { text: 'Other Engines', link: '/docs/frontend/third-party' }, + { text: 'Vite JS', link: '/docs/frontend/vite' }, + { text: 'Tailwind CSS', link: '/docs/frontend/tailwind' }, + { text: 'Inertia JS', link: '/docs/frontend/inertia' }, + // { text: 'Viewi PHP', link: '/docs/frontend/viewi' }, + // { text: 'Leaf UI', link: '/docs/frontend/leaf-ui' }, + ], + }, + { + text: 'Building to scale', + // collapsible: true, + // collapsed: true, + items: [ + { text: 'Leaf + MVC', link: '/docs/mvc/' }, + { text: 'Controllers', link: '/docs/mvc/controllers' }, + // { text: 'Views', link: '/docs/frontend/mvc' }, + { text: 'Models', link: '/docs/database/models' }, + { text: 'Migrations', link: '/docs/database/migrations' }, + { text: 'Schema', link: '/docs/database/schema' }, + { text: 'Seeders', link: '/docs/database/seeders' }, + { text: 'Factories', link: '/docs/database/factories' }, + { text: 'Writing Commands', link: '/docs/mvc/commands' }, + // { text: 'Mailing', link: '/docs/utils/mail/mvc' }, + { text: 'MVC Helpers', link: '/docs/mvc/globals' }, + { text: 'Custom Libraries', link: '/docs/mvc/libraries' }, + { text: 'MVC Console Tool', link: '/docs/mvc/console' }, + ], + }, + // { + // text: 'Codelabs', + // // collapsible: true, + // // collapsed: true, + // items: [ + // { text: 'Intro', link: '/codelabs/' }, + // { text: 'Contributing', link: '/codelabs/contributing' }, + // // { text: 'Deployment', link: '/codelabs/experiments/deployment/' }, + // // { text: 'Authentication', link: '/codelabs/experiments/auth/' }, + // ], + // }, +]; + +export default sidebar; diff --git a/.vitepress/future/files.md b/.vitepress/future/files.md new file mode 100644 index 00000000..9bcc6f66 --- /dev/null +++ b/.vitepress/future/files.md @@ -0,0 +1,159 @@ +# Database Files + +Leaf MVC inherited all the teachings of Laravel and Ruby on Rails, including the use of migrations, seeders, and factories which made creating, testing and seeding databases a breeze. On top of that, Leaf MVC also introduced a new concept called schema files which allowed you to generate migrations from JSON data. While this was a great feature, it was a bit too much for a lot of developers and added to the growing hell of files in your app. To solve this, we've decided to move away from the Rails/Laravel way of doing things and introduce a new way of handling database files in Leaf MVC. + +## What are Database Files? + +Database files are a way to handle migrations, seeders, and factories in a single file. This way, you can easily manage your database structure without having to create multiple files for each operation and without having to repeat yourself all over your app. Database files are written in yaml which makes them incredibly easy to read and write. + +```yml [flights.yml] +defaultId: true +timestamps: true +columns: + to: string + from: string + identifier: string + +seeds: + count: 10 + data: + to: faker.city + from: faker.city + identifier: faker.uuid +``` + +## Creating a Database File + +Aloe comes with a `g:db` command that you can use to generate a database file. You can generate a database file by running: + +```bash:no-line-numbers +php leaf g:db users +``` + +This will create a database file at `app/database/users.yml` which looks like this: + +```yml [users.yml] +defaultId: true +timestamps: true +columns: + name: string + email: + type: string + length: 255 + unique: true + password: string + email_verified_at: timestamp + +seeds: + count: 10 + data: + name: faker.name + email: faker.email + password: "{{ bcrypt('password') }}" +``` + +Breaking down this file, we have: + +- `defaultId`: This is used to set the default id of the table. If set to `true`, the table will have an auto-incrementing id. If set to `false`, the table will not have an id. + +- `timestamps`: This is used to set timestamps on the table. If set to `true`, the table will have `created_at` and `updated_at` columns. If set to `false`, the table will not have timestamps. + +- `columns`: This is used to set the columns of the table. The key is the column name and the value is a key value pair of column properties. The available properties are: + - `type`: The type of the column. This can be `string`, `integer`, `timestamp` or any type supported by Laravel's Eloquent. + - `length`: The length of the column. This is only used for `string` columns. + - `unique`: This is used to set the column as unique. + - `nullable`: This is used to set the column as nullable. + - `default`: This is used to set the default value of the column. + - `autoIncrement`: This is used to set the column as auto-incrementing. + - `unsigned`: This is used to set the column as unsigned. + - `index`: This is used to set the column as an index. + - `primary`: This is used to set the column as the primary key. + - `foreign`: This is used to set the column as a foreign key. The value of this key is the table and column the column is a foreign key to. + - `onDelete`: This is used to set the `ON DELETE` constraint of the foreign key. + - `onUpdate`: This is used to set the `ON UPDATE` constraint of the foreign key. + +- `seeds`: This is used to set the seeders of the table. The available properties are: + - `count`: This is used to set the number of seeds to generate. + - `data`: This is used to set the data of the seeds. The key is the column name and the value is the value of the column. You can use `faker.[value]` to generate fake data for the column. You can also use `{{ [value] }}` to use PHP code. + - `truncate`: This is used to truncate the table before seeding. + +## DB Migrations + +Traditionally, migrations are used to create database tables and modify them. In Leaf MVC, you can create migrations in your database files. The `columns` key in your database file is used to create migrations. Here's an example of a migration: + +```yml [users.yml] +columns: + name: string + email: + type: string + length: 255 + unique: true + password: string + email_verified_at: timestamp +``` + +In this example, we create a migration that creates a `users` table with `name`, `email`, `password`, and `email_verified_at` columns. To run your migrations, you can use the `db:migrate` command: + +```bash:no-line-numbers +php leaf db:migrate +``` + + + +## DB Seeders + +Seeders are used to populate your database with dummy data. In Leaf MVC, you can create seeders in your database files. The `seeders` key in your database file is used to create seeders. Here's an example of a seeder: + +```yml [users.yml] +seeds: + data: + - name: 'Example User' + email: 'example@example.com' + password: "{{ bcrypt('password') }}" +``` + +In this example, we create a seeder that seeds the `users` table with an example user. We are passing an array of seeds to the `data` key, each seed being a key value pair of column name and value. + +If you want to generate multiple seeds, you can pass an object to the `data` key instead of an array together with a `count` key: + +```yml [users.yml] +seeds: + count: 10 + data: + name: 'Example User' + email: 'example@example.com' + password: "{{ bcrypt('password') }}" +``` + +After creating your seeder, you can run your seeders using the `db:seed` command: + +```bash:no-line-numbers +php leaf db:seed +``` + +This will generate 10 seeds for the `users` table with the same data which is not very useful. To generate multiple fake seeds, you can use what other frameworks call a factory. + +In Leaf MVC, factories and seeders are the same thing as we believe this confusion is unnecessary. If you want to generate fake data for your seeders, you can add `faker.[value]` as the value of a column in your seeder. Here's an example: + +```yml{4,5} [users.yml] +seeds: + count: 10 + data: + name: faker.name + email: faker.email + password: "{{ bcrypt('password') }}" +``` + +In this example, we're generating 10 fake seeds for the `users` table. diff --git a/.vitepress/headerMdPlugin.ts b/.vitepress/headerMdPlugin.ts deleted file mode 100644 index 677b9c80..00000000 --- a/.vitepress/headerMdPlugin.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * A markdown-it plugin to support custom header metadata - * Headers that end with * are Options API only - * Headers that end with ** are Composition API only - * This plugin strips the markers and augments the extracted header data, - * which can be then used by the theme to filter headers. - * - * TODO: we will likely also need special syntax for preserving the same anchor - * links across translations similar to the one at - * https://github.com/vitejs/docs-cn/tree/main/.vitepress/markdown-it-custom-anchor - */ - -import MarkdownIt from 'markdown-it' -import { MarkdownRenderer } from 'vitepress' - -declare module 'vitepress' { - interface Header { - compositionOnly?: boolean - optionsOnly?: boolean - } -} - -export const headerPlugin = (md: MarkdownIt) => { - const originalOpen = md.renderer.rules.heading_open! - md.renderer.rules.heading_open = (tokens, i, ...rest) => { - for (const child of tokens[i + 1].children!) { - if (child.type === 'text' && child.content.endsWith('*')) { - child.content = child.content.replace(/\s*\*+$/, '') - } - } - return originalOpen.call(null, tokens, i, ...rest) - } - - md.renderer.rules.heading_close = (tokens, i, options, _env, self) => { - const headers = (md as MarkdownRenderer).__data?.headers - if (headers) { - const last = headers[headers.length - 1] - if (last.title.endsWith('*')) { - if (last.title.endsWith('**')) { - last.compositionOnly = true - } else { - last.optionsOnly = true - } - last.title = last.title.replace(/\s*\*+$/, '') - } - } - return self.renderToken(tokens, i, options) - } -} diff --git a/.vitepress/inlined-scripts/restorePreference.js b/.vitepress/inlined-scripts/restorePreference.js deleted file mode 100644 index e8313f6c..00000000 --- a/.vitepress/inlined-scripts/restorePreference.js +++ /dev/null @@ -1,13 +0,0 @@ -;(() => { - const restore = (key, cls, def = false) => { - const saved = localStorage.getItem(key) - if (saved ? saved !== 'false' : def) { - document.documentElement.classList.add(cls) - } - } - restore('vue-docs-prefer-functional', 'prefer-functional') - restore('vue-docs-prefer-sfc', 'prefer-sfc', true) - - window.__VUE_BANNER_ID__ = 'wip' - restore(`vue-docs-banner-${__VUE_BANNER_ID__}`, 'banner-dismissed') -})() diff --git a/.vitepress/theme/components/Banner.vue b/.vitepress/theme/components/Banner.vue deleted file mode 100644 index 8d4c697e..00000000 --- a/.vitepress/theme/components/Banner.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - diff --git a/.vitepress/theme/components/BlogSection.vue b/.vitepress/theme/components/Community/Blog.vue similarity index 68% rename from .vitepress/theme/components/BlogSection.vue rename to .vitepress/theme/components/Community/Blog.vue index 901f1d76..3a339772 100644 --- a/.vitepress/theme/components/BlogSection.vue +++ b/.vitepress/theme/components/Community/Blog.vue @@ -1,62 +1,29 @@ diff --git a/.vitepress/theme/components/Community/Community.vue b/.vitepress/theme/components/Community/Community.vue new file mode 100644 index 00000000..54e95507 --- /dev/null +++ b/.vitepress/theme/components/Community/Community.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/events/components/PartnerCard.vue b/.vitepress/theme/components/Community/Events/EventCard.vue similarity index 88% rename from src/events/components/PartnerCard.vue rename to .vitepress/theme/components/Community/Events/EventCard.vue index 24c14bcf..5ff7b6e8 100644 --- a/src/events/components/PartnerCard.vue +++ b/.vitepress/theme/components/Community/Events/EventCard.vue @@ -1,7 +1,7 @@