├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── issue_template.md └── pull_request_template.md ├── .gitignore ├── .prettierignore ├── README.md ├── adonisrc.ts ├── assets ├── app.css ├── app.js └── og_template.svg ├── bin ├── build.ts ├── download_sponsors.ts └── serve.ts ├── content ├── config.json ├── docs │ ├── ace │ │ ├── ace_help_screen.jpeg │ │ ├── ace_help_screen_original.png │ │ ├── ace_repl.png │ │ ├── ace_repl_original.png │ │ ├── args.md │ │ ├── creating_commands.md │ │ ├── flags.md │ │ ├── introduction.md │ │ ├── prompts.md │ │ └── tui.md │ ├── authentication │ │ ├── access_tokens_guard.md │ │ ├── basic_auth_guard.md │ │ ├── custom_auth_guard.md │ │ ├── introduction.md │ │ ├── session_guard.md │ │ ├── social_authentication.md │ │ └── verifying_user_credentials.md │ ├── basics │ │ ├── attach-debug.mp4 │ │ ├── body_parser.md │ │ ├── browser-dd.png │ │ ├── controllers.md │ │ ├── cookies.md │ │ ├── dd-cli.png │ │ ├── dd.png │ │ ├── dd_browser_output.png │ │ ├── dd_original.png │ │ ├── debug-dev-server-original.png │ │ ├── debug-dev-server.png │ │ ├── debug-sql-queries-original.png │ │ ├── debug-sql-queries.png │ │ ├── debugging.md │ │ ├── exception_handling.md │ │ ├── file_uploads.md │ │ ├── middleware.md │ │ ├── middleware_flow 2.jpeg │ │ ├── middleware_flow.jpeg │ │ ├── post_comments_resource_routes_list.png │ │ ├── post_comments_resource_routes_list_original.png │ │ ├── post_resource_routes_list.png │ │ ├── post_resource_routes_list_original.png │ │ ├── request.md │ │ ├── response.md │ │ ├── routing.md │ │ ├── session.md │ │ ├── shallow_routes_list.png │ │ ├── shallow_routes_list_original.jpg │ │ ├── static_file_server.md │ │ ├── validation.md │ │ ├── vite.md │ │ ├── vscode_routes_list.png │ │ └── vscode_routes_list_original.png │ ├── concepts │ │ ├── adonisrc_file.md │ │ ├── application.md │ │ ├── application_lifecycle.md │ │ ├── assembler_hooks.md │ │ ├── async_local_storage.md │ │ ├── boot_phase_flow_chart.png │ │ ├── boot_phase_flow_chart_original.png │ │ ├── config_providers.md │ │ ├── container_services.md │ │ ├── dependency_injection.md │ │ ├── extending_the_framework.md │ │ ├── hmr.md │ │ ├── http_context.md │ │ ├── http_overview.md │ │ ├── scaffolding.md │ │ ├── scaffolding_workflow.png │ │ ├── scaffolding_workflow_original.png │ │ ├── server_boot_lifecycle.png │ │ ├── server_boot_lifecycle_original.png │ │ ├── service_providers.md │ │ ├── start_phase_flow_chart.png │ │ ├── start_phase_flow_chart_original.png │ │ ├── termination_phase_flow_chart.png │ │ ├── termination_phase_flow_chart_original.png │ │ ├── tooling_config.md │ │ └── typescript_build_process.md │ ├── database │ │ ├── introduction.md │ │ ├── lucid.md │ │ └── redis.md │ ├── db.json │ ├── digging_deeper │ │ ├── ally_autocomplete.png │ │ ├── cache.md │ │ ├── drive.md │ │ ├── emitter.md │ │ ├── health_checks.md │ │ ├── i18n.md │ │ ├── locks.md │ │ ├── logger.md │ │ ├── mail.md │ │ ├── repl.md │ │ └── transmit.md │ ├── getting_started │ │ ├── configuration.md │ │ ├── deployment.md │ │ ├── env_intellisense.jpeg │ │ ├── env_intellisense_original.png │ │ ├── environment_variables.md │ │ ├── folder_structure.md │ │ └── installation.md │ ├── old │ │ ├── forms_and_validation │ │ │ ├── custom_messages.md │ │ │ ├── pika-1675149043774-1x.jpeg │ │ │ ├── schema_101.md │ │ │ ├── validating_api_requests.md │ │ │ ├── validating_server_rendered_forms.md │ │ │ ├── validation.md │ │ │ └── validator-static-types.webp │ │ ├── fundamentals │ │ │ └── package_development.md │ │ ├── http │ │ │ └── assets_bundling_encore.md │ │ ├── pages │ │ │ └── adonisjs-at-a-glance.md │ │ ├── resources │ │ │ └── updates.md │ │ ├── tutorial │ │ │ ├── creating-new-application.md │ │ │ └── introduction.md │ │ └── validator │ │ │ ├── custom_messages.md │ │ │ ├── custom_rules.md │ │ │ ├── error_reporters.md │ │ │ ├── introduction.md │ │ │ ├── schema_caching.md │ │ │ ├── schema_types.md │ │ │ └── validation_rules.md │ ├── preface │ │ ├── contribution_guide.md │ │ ├── faqs.md │ │ ├── governance.md │ │ ├── introduction.md │ │ └── releases.md │ ├── references │ │ ├── commands.md │ │ ├── edge.md │ │ ├── events.md │ │ ├── exceptions.md │ │ └── helpers.md │ ├── security │ │ ├── authorization.md │ │ ├── cors.md │ │ ├── encryption.md │ │ ├── hashing.md │ │ ├── rate_limiting.md │ │ └── securing_ssr_applications.md │ ├── testing │ │ ├── browser_tests.md │ │ ├── browser_tests_output.jpeg │ │ ├── browser_tests_output_original.png │ │ ├── console_tests.md │ │ ├── database.md │ │ ├── http_tests.md │ │ ├── introduction.md │ │ └── mocks_and_fakes.md │ └── views-and-templates │ │ ├── edgejs.md │ │ ├── inertia.md │ │ └── introduction.md ├── releases.json └── sponsors.json ├── package.json ├── public ├── _redirects ├── icons │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── favicon.svg │ ├── mstile-150x150.png │ ├── safari-pinned-tab.svg │ └── site.webmanifest └── og │ ├── ace_args.png │ ├── ace_creating_commands.png │ ├── ace_flags.png │ ├── ace_introduction.png │ ├── ace_prompts.png │ ├── ace_tui.png │ ├── authentication_access_tokens_guard.png │ ├── authentication_basic_auth_guard.png │ ├── authentication_custom_auth_guard.png │ ├── authentication_introduction.png │ ├── authentication_session_guard.png │ ├── authentication_social_authentication.png │ ├── authentication_verifying_user_credentials.png │ ├── basics_body_parser.png │ ├── basics_controllers.png │ ├── basics_cookies.png │ ├── basics_exception_handling.png │ ├── basics_file_uploads.png │ ├── basics_middleware.png │ ├── basics_request.png │ ├── basics_response.png │ ├── basics_routing.png │ ├── basics_session.png │ ├── basics_static_file_server.png │ ├── basics_validation.png │ ├── basics_vite.png │ ├── concepts_adonisrc_file.png │ ├── concepts_application.png │ ├── concepts_application_lifecycle.png │ ├── concepts_assembler_hooks.png │ ├── concepts_async_local_storage.png │ ├── concepts_config_providers.png │ ├── concepts_container_services.png │ ├── concepts_dependency_injection.png │ ├── concepts_extending_the_framework.png │ ├── concepts_hmr.png │ ├── concepts_http_context.png │ ├── concepts_http_overview.png │ ├── concepts_scaffolding.png │ ├── concepts_service_providers.png │ ├── concepts_tooling_config.png │ ├── concepts_typescript_build_process.png │ ├── database_introduction.png │ ├── database_lucid.png │ ├── database_redis.png │ ├── digging_deeper_emitter.png │ ├── digging_deeper_i18n.png │ ├── digging_deeper_locks.png │ ├── digging_deeper_logger.png │ ├── digging_deeper_mail.png │ ├── digging_deeper_repl.png │ ├── digging_deeper_transmit.png │ ├── getting_started_configuration.png │ ├── getting_started_deployment.png │ ├── getting_started_environment_variables.png │ ├── getting_started_folder_structure.png │ ├── getting_started_installation.png │ ├── preface_contribution_guide.png │ ├── preface_faqs.png │ ├── preface_governance.png │ ├── preface_introduction.png │ ├── preface_releases.png │ ├── references_commands.png │ ├── references_edge.png │ ├── references_events.png │ ├── references_exceptions.png │ ├── references_helpers.png │ ├── security_authorization.png │ ├── security_cors.png │ ├── security_encryption.png │ ├── security_hashing.png │ ├── security_rate_limiting.png │ ├── security_securing_ssr_applications.png │ ├── testing_browser_tests.png │ ├── testing_console_tests.png │ ├── testing_database.png │ ├── testing_http_tests.png │ ├── testing_introduction.png │ ├── testing_mocks_and_fakes.png │ ├── views_and-templates-edgejs.png │ ├── views_and-templates-inertia.png │ └── views_and-templates-introduction.png ├── src ├── bootstrap.ts └── collections.ts ├── templates ├── authenticate.edge ├── docs.edge ├── elements │ └── img.edge ├── layouts │ └── main.edge ├── logos │ └── sponsors │ │ ├── cleavr.edge │ │ ├── ezycourse.edge │ │ ├── galaxy.edge │ │ ├── lambdatest.edge │ │ ├── relancer.edge │ │ ├── route4me.edge │ │ └── toolzz.edge └── partials │ ├── arrow.edge │ ├── detect_color_mode.edge │ ├── featured_sponsors.edge │ ├── introduction_cards.edge │ ├── logo.edge │ ├── logo_mobile.edge │ ├── pagination.edge │ ├── recent_releases.edge │ ├── releases.edge │ └── sponsors.edge ├── tsconfig.json ├── vite.config.js └── vscode_grammars ├── dotenv.tmLanguage.json ├── edge.tmLanguage.json └── main.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.json] 12 | insert_final_newline = ignore 13 | 14 | [**.min.js] 15 | indent_style = ignore 16 | insert_final_newline = ignore 17 | 18 | [MakeFile] 19 | indent_style = space 20 | 21 | [*.md] 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_template.md: -------------------------------------------------------------------------------- 1 | # Please describe the issue here 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies and AdonisJS build 2 | node_modules 3 | build 4 | 5 | # Secrets 6 | .env 7 | .env.local 8 | .env.production.local 9 | .env.development.local 10 | 11 | # Frontend assets compiled code 12 | public/hot.json 13 | public/build 14 | public/assets 15 | 16 | # Build tools specific 17 | npm-debug.log 18 | yarn-error.log 19 | 20 | # Editors specific 21 | .fleet 22 | .idea 23 | .vscode 24 | 25 | # Static export 26 | dist 27 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md 2 | -------------------------------------------------------------------------------- /adonisrc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@adonisjs/core/app' 2 | 3 | export default defineConfig({ 4 | typescript: true, 5 | directories: { 6 | views: 'templates', 7 | }, 8 | providers: [ 9 | () => import('@adonisjs/core/providers/app_provider'), 10 | () => import('@adonisjs/core/providers/edge_provider'), 11 | () => import('@adonisjs/vite/vite_provider'), 12 | () => import('@adonisjs/static/static_provider'), 13 | ], 14 | metaFiles: [ 15 | { 16 | pattern: './public/**/*', 17 | reloadServer: false, 18 | }, 19 | ], 20 | }) 21 | -------------------------------------------------------------------------------- /assets/app.js: -------------------------------------------------------------------------------- 1 | import 'unpoly' 2 | import Alpine from 'alpinejs' 3 | import mediumZoom from 'medium-zoom' 4 | import docsearch from '@docsearch/js' 5 | import { tabs } from 'edge-uikit/tabs' 6 | import Persist from '@alpinejs/persist' 7 | import collapse from '@alpinejs/collapse' 8 | 9 | import { 10 | initZoomComponent, 11 | initBaseComponents, 12 | initSearchComponent, 13 | } from '@dimerapp/docs-theme/scripts' 14 | 15 | import.meta.glob(['../content/**/*.png', '../content/**/*.jpeg', '../content/**/*.jpg']) 16 | 17 | Alpine.plugin(tabs) 18 | Alpine.plugin(Persist) 19 | Alpine.plugin(collapse) 20 | Alpine.plugin(initBaseComponents) 21 | Alpine.plugin(initSearchComponent(docsearch)) 22 | Alpine.plugin(initZoomComponent(mediumZoom)) 23 | Alpine.start() 24 | 25 | up.on('up:fragment:offline', function (event) { 26 | window.location.reload() 27 | }) 28 | -------------------------------------------------------------------------------- /bin/build.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Development server entrypoint 4 | |-------------------------------------------------------------------------- 5 | | 6 | | The "server.ts" file is the entrypoint for starting the AdonisJS HTTP 7 | | server. Either you can run this file directly or use the "serve" 8 | | command to run this file and monitor file changes 9 | | 10 | */ 11 | 12 | import 'reflect-metadata' 13 | import sharp from 'sharp' 14 | import { Ignitor } from '@adonisjs/core' 15 | import { defineConfig } from '@adonisjs/vite' 16 | import { Collection } from '@dimerapp/content' 17 | import { mkdir, readFile } from 'node:fs/promises' 18 | import { joinToURL } from '@adonisjs/core/helpers' 19 | import { existsSync, writeFileSync } from 'node:fs' 20 | import string from '@adonisjs/core/helpers/string' 21 | import { join, relative } from 'node:path' 22 | import { fileURLToPath } from 'node:url' 23 | 24 | /** 25 | * URL to the application root. AdonisJS need it to resolve 26 | * paths to file and directories for scaffolding commands 27 | */ 28 | const APP_ROOT = new URL('../', import.meta.url) 29 | 30 | /** 31 | * The importer is used to import files in context of the 32 | * application. 33 | */ 34 | const IMPORTER = (filePath: string) => { 35 | if (filePath.startsWith('./') || filePath.startsWith('../')) { 36 | return import(new URL(filePath, APP_ROOT).href) 37 | } 38 | return import(filePath) 39 | } 40 | 41 | const ogTemplate = await readFile(joinToURL(import.meta.url, '../assets/og_template.svg'), 'utf-8') 42 | 43 | async function generateOgImage(entry: ReturnType[0], htmlOutput: string) { 44 | /** 45 | * Read the HTML file to extract the meta description and category 46 | * This is super hackish but works for now. Ideally, we should be able to extract 47 | * frontmatter from markdown files, I think Dimmer doesn't support it yet. 48 | */ 49 | const htmlLocation = joinToURL(import.meta.url, '../', 'dist', htmlOutput) 50 | const html = await readFile(htmlLocation, 'utf-8') 51 | const metaDescription = html.match(//) 52 | const description = metaDescription ? metaDescription[1] : '' 53 | const category = html.match( 54 | // 55 | ) 56 | 57 | /** 58 | * Create the og directory when missing 59 | */ 60 | if (!existsSync('public/og')) await mkdir('public/og', { recursive: true }) 61 | 62 | /** 63 | * Generate the output filename 64 | */ 65 | const relativePath = relative(join(fileURLToPath(APP_ROOT), 'content/docs'), entry.contentPath) 66 | const filename = relativePath.replace('.md', '').replace(/\//g, '-').replace('-', '_') 67 | 68 | /** 69 | * Skip generating the og image when it already exists 70 | */ 71 | const output = `public/og/${filename}.png` 72 | let shouldGenerateOgImage = true 73 | if (existsSync(output)) { 74 | shouldGenerateOgImage = false 75 | } 76 | 77 | /** 78 | * Take 3 lines of 40 characters each 79 | * insert them in the svg template 80 | */ 81 | const lines = description 82 | .trim() 83 | .split(/(.{0,60})(?:\s|$)/g) 84 | .filter(Boolean) 85 | 86 | const svg = ogTemplate 87 | .replace( 88 | '{{ category }}', 89 | category ? string.titleCase(category[1].replaceAll('-', ' ')) : 'Docs' 90 | ) 91 | .replace('{{ title }}', string.encodeSymbols(entry.title.slice(0, 24))) 92 | .replace('{{ line1 }}', lines[0]) 93 | .replace('{{ line2 }}', lines[1] || '') 94 | .replace('{{ line3 }}', lines[2] || '') 95 | 96 | try { 97 | if (shouldGenerateOgImage) { 98 | await sharp(Buffer.from(svg)) 99 | .resize(1200 * 1.1, 630 * 1.1) 100 | .png() 101 | .toFile(output) 102 | } 103 | } catch (e) { 104 | console.error('Failed to generate og image for %s', entry.title, e) 105 | } 106 | 107 | /** 108 | * Insert the og:image and twitter:image meta tags 109 | */ 110 | const ogImageUrl = output.replace('public/', 'https://docs.adonisjs.com/') 111 | const tags = ` 112 | 113 | 114 | 115 | ` 116 | 117 | const updatedHtml = html.replace('', `${tags}`) 118 | writeFileSync(htmlLocation, updatedHtml) 119 | } 120 | 121 | /** 122 | * Exports collection to HTML files 123 | */ 124 | async function exportHTML() { 125 | const { collections } = await import('#src/collections') 126 | const { default: ace } = await import('@adonisjs/core/services/ace') 127 | const { default: app } = await import('@adonisjs/core/services/app') 128 | 129 | for (let collection of collections) { 130 | for (let entry of collection.all()) { 131 | try { 132 | const output = await entry.writeToDisk(app.makePath('dist'), { collection, entry }) 133 | await generateOgImage(entry, output.filePath) 134 | ace.ui.logger.action(`create ${output.filePath}`).succeeded() 135 | } catch (error) { 136 | ace.ui.logger.action(`create ${entry.permalink}`).failed(error) 137 | } 138 | } 139 | } 140 | } 141 | 142 | const application = new Ignitor(APP_ROOT, { importer: IMPORTER }) 143 | .tap((app) => { 144 | app.initiating(() => { 145 | app.useConfig({ 146 | appUrl: process.env.APP_URL || '', 147 | app: { 148 | appKey: 'zKXHe-Ahdb7aPK1ylAJlRgTefktEaACi', 149 | http: {}, 150 | }, 151 | logger: { 152 | default: 'app', 153 | loggers: { 154 | app: { 155 | enabled: true, 156 | }, 157 | }, 158 | }, 159 | vite: defineConfig({}), 160 | }) 161 | }) 162 | }) 163 | .createApp('console') 164 | 165 | await application.init() 166 | await application.boot() 167 | await application.start(exportHTML) 168 | -------------------------------------------------------------------------------- /bin/download_sponsors.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Script to download sponsors 4 | |-------------------------------------------------------------------------- 5 | | 6 | | This script downloads the sponsors JSON from the pre-configured URLs 7 | | configured inside the "content/config.json" file. 8 | | 9 | */ 10 | 11 | import { request } from 'undici' 12 | import { readFile, writeFile } from 'node:fs/promises' 13 | 14 | /** 15 | * The file path to the config.json file 16 | */ 17 | const CONFIG_FILE_PATH = new URL('../content/config.json', import.meta.url) 18 | 19 | /** 20 | * The file path to the sponsors.json file. The output will be written 21 | * here. 22 | */ 23 | const SPONSORS_FILE_PATH = new URL('../content/sponsors.json', import.meta.url) 24 | 25 | export async function downloadSponsors() { 26 | console.log('starting to download sponsors...') 27 | 28 | try { 29 | const fileContents = await readFile(CONFIG_FILE_PATH, 'utf-8') 30 | const sources = JSON.parse(fileContents).sponsors_sources 31 | let sponsorsList: any[] = [] 32 | 33 | /** 34 | * No sources configured. So going to create an empty 35 | * sponsors.json file. 36 | */ 37 | if (sources.length === 0) { 38 | console.log('skipping download. No sources found...') 39 | await writeFile(SPONSORS_FILE_PATH, JSON.stringify(sponsorsList)) 40 | return 41 | } 42 | 43 | /** 44 | * Processing sponsors 45 | */ 46 | for (let source of sources) { 47 | const { body } = await request(source) 48 | const sponsors = await body.json() 49 | if (Array.isArray(sponsors)) { 50 | sponsorsList = sponsorsList.concat(sponsors) 51 | console.log(`Downloaded "${sponsors.length} sponsors" from "${source}"`) 52 | } 53 | } 54 | 55 | await writeFile(SPONSORS_FILE_PATH, JSON.stringify(sponsorsList)) 56 | } catch (error) { 57 | if (error.code === 'ENOENT') { 58 | console.warn('Cannot download sponsors list. Unable to find "content/config.json" file') 59 | return 60 | } 61 | 62 | console.error(error) 63 | } 64 | } 65 | 66 | await downloadSponsors() 67 | -------------------------------------------------------------------------------- /bin/serve.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Development server entrypoint 4 | |-------------------------------------------------------------------------- 5 | | 6 | | The "server.ts" file is the entrypoint for starting the AdonisJS HTTP 7 | | server. Either you can run this file directly or use the "serve" 8 | | command to run this file and monitor file changes 9 | | 10 | */ 11 | 12 | import 'reflect-metadata' 13 | import { Ignitor } from '@adonisjs/core' 14 | import { readFile } from 'node:fs/promises' 15 | import { defineConfig } from '@adonisjs/vite' 16 | import type { ApplicationService } from '@adonisjs/core/types' 17 | import { defineConfig as defineHttpConfig } from '@adonisjs/core/http' 18 | 19 | /** 20 | * URL to the application root. AdonisJS need it to resolve 21 | * paths to file and directories for scaffolding commands 22 | */ 23 | const APP_ROOT = new URL('../', import.meta.url) 24 | 25 | /** 26 | * The importer is used to import files in context of the 27 | * application. 28 | */ 29 | const IMPORTER = (filePath: string) => { 30 | if (filePath.startsWith('./') || filePath.startsWith('../')) { 31 | return import(new URL(filePath, APP_ROOT).href) 32 | } 33 | return import(filePath) 34 | } 35 | 36 | /** 37 | * Defining routes for development server 38 | */ 39 | async function defineRoutes(app: ApplicationService) { 40 | const { default: server } = await import('@adonisjs/core/services/server') 41 | const { collections } = await import('#src/collections') 42 | const { default: router } = await import('@adonisjs/core/services/router') 43 | 44 | server.use([() => import('@adonisjs/static/static_middleware')]) 45 | const redirects = await readFile(app.publicPath('_redirects'), 'utf-8') 46 | const redirectsCollection = redirects.split('\n').reduce( 47 | (result, line) => { 48 | const [from, to] = line.split(' ') 49 | result[from] = to 50 | return result 51 | }, 52 | {} as Record 53 | ) 54 | 55 | router.get('*', async ({ request, response }) => { 56 | if (redirectsCollection[request.url()]) { 57 | return response.redirect(redirectsCollection[request.url()]) 58 | } 59 | 60 | for (let collection of collections) { 61 | await collection.refresh() 62 | const entry = collection.findByPermalink(request.url()) 63 | if (entry) { 64 | return entry.render({ collection, entry }).catch((error) => { 65 | console.log(error) 66 | }) 67 | } 68 | } 69 | 70 | return response.notFound('Page not found') 71 | }) 72 | } 73 | 74 | new Ignitor(APP_ROOT, { importer: IMPORTER }) 75 | .tap((app) => { 76 | app.initiating(() => { 77 | app.useConfig({ 78 | appUrl: process.env.APP_URL || '', 79 | app: { 80 | appKey: 'zKXHe-Ahdb7aPK1ylAJlRgTefktEaACi', 81 | http: defineHttpConfig({}), 82 | }, 83 | static: { 84 | enabled: true, 85 | etag: true, 86 | lastModified: true, 87 | dotFiles: 'ignore', 88 | }, 89 | logger: { 90 | default: 'app', 91 | loggers: { 92 | app: { 93 | enabled: true, 94 | }, 95 | }, 96 | }, 97 | vite: defineConfig({}), 98 | }) 99 | }) 100 | 101 | app.starting(defineRoutes) 102 | }) 103 | .httpServer() 104 | .start() 105 | .catch(console.error) 106 | -------------------------------------------------------------------------------- /content/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu": [ 3 | { 4 | "href": "https://v6-migration.adonisjs.com", 5 | "title": "Migration Guide" 6 | }, 7 | { 8 | "href": "https://v5-docs.adonisjs.com", 9 | "title": "V5 Documentation" 10 | } 11 | ], 12 | "links": { 13 | "home": { 14 | "title": "AdonisJS", 15 | "href": "/" 16 | }, 17 | "github": { 18 | "title": "AdonisJS on GitHub", 19 | "href": "https://github.com/adonisjs/core" 20 | } 21 | }, 22 | "search": { 23 | "appId": "KXECYAMEX8", 24 | "indexName": "adonisjs_next", 25 | "apiKey": "01279e9ede105d87a1ade54565b1a2fd" 26 | }, 27 | "sponsors_sources": [ 28 | "https://raw.githubusercontent.com/thetutlage/static/main/sponsorkit/sponsors.json" 29 | ], 30 | "featured_sponsors": [ 31 | { 32 | "name": "Vehicle Routing and Route Optimization Software", 33 | "website": "https://route4me.com/", 34 | "logo": "logos/sponsors/route4me" 35 | }, 36 | { 37 | "name": "Ezycourse", 38 | "website": "https://ezycourse.com/", 39 | "logo": "logos/sponsors/ezycourse" 40 | }, 41 | { 42 | "name": "Galaxy Cloud", 43 | "website": "https://meteor.software/g6h", 44 | "logo": "logos/sponsors/galaxy" 45 | }, 46 | { 47 | "name": "LambdaTest", 48 | "website": "https://www.lambdatest.com", 49 | "logo": "logos/sponsors/lambdatest" 50 | }, 51 | { 52 | "name": "Relancer", 53 | "website": "https://relancer.com/", 54 | "logo": "logos/sponsors/relancer" 55 | } 56 | ], 57 | "carbon_ads": { 58 | "url": "//cdn.carbonads.com/carbon.js?serve=CKYIL23I&placement=docsadonisjscom" 59 | }, 60 | "fileEditBaseUrl": "https://github.com/adonisjs/v6-docs/blob/main", 61 | "copyright": "AdonisJS" 62 | } 63 | -------------------------------------------------------------------------------- /content/docs/ace/ace_help_screen.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/ace/ace_help_screen.jpeg -------------------------------------------------------------------------------- /content/docs/ace/ace_help_screen_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/ace/ace_help_screen_original.png -------------------------------------------------------------------------------- /content/docs/ace/ace_repl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/ace/ace_repl.png -------------------------------------------------------------------------------- /content/docs/ace/ace_repl_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/ace/ace_repl_original.png -------------------------------------------------------------------------------- /content/docs/ace/args.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn about defining and processing command arguments in Ace commands. 3 | --- 4 | 5 | # Command arguments 6 | 7 | Arguments refer to the positional arguments mentioned after the command name. Since arguments are positional, passing them in the correct order is necessary. 8 | 9 | You must define command arguments as class properties and decorate them using the `args` decorator. The arguments will be accepted in the same order as they are defined in the class. 10 | 11 | In the following example, we use the `@args.string` decorator to define an argument that accepts a string value. 12 | 13 | ```ts 14 | import { BaseCommand, args, flags } from '@adonisjs/core/ace' 15 | 16 | export default class GreetCommand extends BaseCommand { 17 | static commandName = 'greet' 18 | static description = 'Greet a user by name' 19 | 20 | @args.string() 21 | declare name: string 22 | 23 | run() { 24 | console.log(this.name) 25 | } 26 | } 27 | ``` 28 | 29 | To accept multiple values under the same argument name, you may use the `@args.spread` decorator. Do note, the spread argument must be the last. 30 | 31 | ```ts 32 | import { BaseCommand, args, flags } from '@adonisjs/core/ace' 33 | 34 | export default class GreetCommand extends BaseCommand { 35 | static commandName = 'greet' 36 | static description = 'Greet a user by name' 37 | 38 | // highlight-start 39 | @args.spread() 40 | declare names: string[] 41 | // highlight-start 42 | 43 | run() { 44 | console.log(this.names) 45 | } 46 | } 47 | ``` 48 | 49 | ## Argument name and description 50 | 51 | The argument name is displayed on the help screen. By default, the argument name is a dashed case representation of the class property name. However, you can define a custom value as well. 52 | 53 | ```ts 54 | @args.string({ 55 | argumentName: 'user-name' 56 | }) 57 | declare name: string 58 | ``` 59 | 60 | The argument description is shown on the help screen and can be set using the `description` option. 61 | 62 | ```ts 63 | @args.string({ 64 | argumentName: 'user-name', 65 | description: 'Name of the user' 66 | }) 67 | declare name: string 68 | ``` 69 | 70 | ## Optional arguments with a default value 71 | 72 | By default, all arguments are required. However, you can make them optional by setting the `required` option to `false`. The optional arguments must be at the end. 73 | 74 | ```ts 75 | @args.string({ 76 | description: 'Name of the user', 77 | required: false, 78 | }) 79 | declare name?: string 80 | ``` 81 | 82 | You may set the default value of an optional argument using the `default` property. 83 | 84 | ```ts 85 | @args.string({ 86 | description: 'Name of the user', 87 | required: false, 88 | default: 'guest' 89 | }) 90 | declare name: string 91 | ``` 92 | 93 | ## Processing argument value 94 | 95 | Using the `parse` method, you can process the argument value before it is defined as the class property. 96 | 97 | ```ts 98 | @args.string({ 99 | argumentName: 'user-name', 100 | description: 'Name of the user', 101 | parse (value) { 102 | return value ? value.toUpperCase() : value 103 | } 104 | }) 105 | declare name: string 106 | ``` 107 | 108 | ## Accessing all arguments 109 | 110 | You can access all the arguments mentioned while running the command using the `this.parsed.args` property. 111 | 112 | ```ts 113 | import { BaseCommand, args, flags } from '@adonisjs/core/ace' 114 | 115 | export default class GreetCommand extends BaseCommand { 116 | static commandName = 'greet' 117 | static description = 'Greet a user by name' 118 | 119 | @args.string() 120 | declare name: string 121 | 122 | run() { 123 | console.log(this.parsed.args) 124 | } 125 | } 126 | ``` 127 | -------------------------------------------------------------------------------- /content/docs/ace/flags.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn how to define and process command flags in Ace commands. 3 | --- 4 | 5 | # Command flags 6 | 7 | Flags are named parameters mentioned with either two hyphens (`--`) or a single hyphen (`-`) (known as the flag alias). The flags can be mentioned in any order. 8 | 9 | You must define flags as class properties and decorate them using the `@flags` decorator. In the following example, we define `resource` and `singular` flags, and both represent a boolean value. 10 | 11 | ```ts 12 | import { BaseCommand, flags } from '@adonisjs/core/ace' 13 | 14 | export default class MakeControllerCommand extends BaseCommands { 15 | @flags.boolean() 16 | declare resource: boolean 17 | 18 | @flags.boolean() 19 | declare singular: boolean 20 | } 21 | ``` 22 | 23 | ## Flag types 24 | 25 | Ace allows defining flags for one of the following types. 26 | 27 | ### Boolean flag 28 | 29 | A boolean flag is defined using the `@flags.boolean` decorator. Mentioning the flag will set its value to `true`. Otherwise, the flag value is `undefined`. 30 | 31 | ```sh 32 | make:controller --resource 33 | 34 | # this.resource === true 35 | ``` 36 | 37 | ```sh 38 | make:controller 39 | 40 | # this.resource === undefined 41 | ``` 42 | 43 | ```sh 44 | make:controller --no-resource 45 | 46 | # this.resource === false 47 | ``` 48 | 49 | The last example shows that the boolean flags can be negated with the `--no-` prefix. 50 | 51 | By default, the negated option is not shown in the help output. However, you may enable it using the `showNegatedVariantInHelp` option. 52 | 53 | ```ts 54 | export default class MakeControllerCommand extends BaseCommands { 55 | @flags.boolean({ 56 | showNegatedVariantInHelp: true, 57 | }) 58 | declare resource: boolean 59 | } 60 | ``` 61 | 62 | ### String flag 63 | 64 | A string flag accepts a value after the flag name. You may define a string flag using the `@flags.string` method. 65 | 66 | ```ts 67 | import { BaseCommand, flags } from '@adonisjs/core/ace' 68 | 69 | export default class MakeControllerCommand extends BaseCommands { 70 | @flags.string() 71 | declare model: string 72 | } 73 | ``` 74 | 75 | ```sh 76 | make:controller --model user 77 | 78 | # this.model = 'user' 79 | ``` 80 | 81 | If the flag value has spaces or special characters, it must be wrapped inside the quotes `""`. 82 | 83 | ```sh 84 | make:controller --model blog user 85 | # this.model = 'blog' 86 | 87 | make:controller --model "blog user" 88 | # this.model = 'blog user' 89 | ``` 90 | 91 | An error is displayed if the flag is mentioned but no value is provided (even when the flag is optional). 92 | 93 | ```sh 94 | make:controller 95 | # Works! The optional flag is not mentioned 96 | 97 | make:controller --model 98 | # Error! Missing value 99 | ``` 100 | 101 | ### Number flag 102 | 103 | The parsing of a number flag is similar to the string flag. However, the value is validated to ensure it is a valid number. 104 | 105 | You can create a number flag using the `@flags.number` decorator. 106 | 107 | ```ts 108 | import { BaseCommand, flags } from '@adonisjs/core/ace' 109 | 110 | export default class MakeUserCommand extends BaseCommands { 111 | @flags.number() 112 | declare score: number 113 | } 114 | ``` 115 | 116 | ### Array flag 117 | 118 | The array flag allows the usage of the flag multiple times when running a command. You can define an array flag using the `@flags.array` method. 119 | 120 | ```ts 121 | import { BaseCommand, flags } from '@adonisjs/core/ace' 122 | 123 | export default class MakeUserCommand extends BaseCommands { 124 | @flags.array() 125 | declare groups: string[] 126 | } 127 | ``` 128 | 129 | ```sh 130 | make:user --groups=admin --groups=moderators --groups=creators 131 | 132 | # this.groups = ['admin', 'moderators', 'creators'] 133 | ``` 134 | 135 | ## Flag name and description 136 | 137 | By default, the flag name is a dashed case representation of the class property name. However, you can define a custom name via the `flagName` option. 138 | 139 | ```ts 140 | @flags.boolean({ 141 | flagName: 'server' 142 | }) 143 | declare startServer: boolean 144 | ``` 145 | 146 | The flag description is displayed on the help screen. You can define it using the `description` option. 147 | 148 | ```ts 149 | @flags.boolean({ 150 | flagName: 'server', 151 | description: 'Starts the application server' 152 | }) 153 | declare startServer: boolean 154 | ``` 155 | 156 | ## Flag aliases 157 | 158 | Aliases the shorthand names for a flag mentioned using a single hyphen (`-`). An alias must be a single character. 159 | 160 | ```ts 161 | @flags.boolean({ 162 | alias: ['r'] 163 | }) 164 | declare resource: boolean 165 | 166 | @flags.boolean({ 167 | alias: ['s'] 168 | }) 169 | declare singular: boolean 170 | ``` 171 | 172 | Flag aliases can be combined when running the command. 173 | 174 | ```ts 175 | make:controller -rs 176 | 177 | # Same as 178 | make:controller --resource --singular 179 | ``` 180 | 181 | ## Default value 182 | 183 | You can define the default value for a flag using the `default` option. The default value is used when the flag is not mentioned or mentioned without a value. 184 | 185 | ```ts 186 | @flags.boolean({ 187 | default: true, 188 | }) 189 | declare startServer: boolean 190 | 191 | @flags.string({ 192 | default: 'sqlite', 193 | }) 194 | declare connection: string 195 | ``` 196 | 197 | 198 | ## Processing flag value 199 | 200 | Using the `parse` method, you can process the flag value before it is defined as the class property. 201 | 202 | ```ts 203 | @flags.string({ 204 | parse (value) { 205 | return value ? connections[value] : value 206 | } 207 | }) 208 | declare connection: string 209 | ``` 210 | 211 | ## Accessing all flags 212 | 213 | You can access all flags mentioned while running the command using the `this.parsed.flags` property. The flags property is an object of key-value pair. 214 | 215 | ```ts 216 | import { BaseCommand, flags } from '@adonisjs/core/ace' 217 | 218 | export default class MakeControllerCommand extends BaseCommands { 219 | @flags.boolean() 220 | declare resource: boolean 221 | 222 | @flags.boolean() 223 | declare singular: boolean 224 | 225 | async run() { 226 | console.log(this.parsed.flags) 227 | 228 | /** 229 | * Names of flags mentioned but not 230 | * accepted by the command 231 | */ 232 | console.log(this.parsed.unknownFlags) 233 | } 234 | } 235 | ``` 236 | -------------------------------------------------------------------------------- /content/docs/ace/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Ace is a command line framework used by AdonisJS to create and run console commands. 3 | --- 4 | 5 | 6 | # Introduction 7 | 8 | Ace is a command line framework used by AdonisJS to create and run console commands. The entry point file for Ace is stored in the root of your project, and you can execute it as follows. 9 | 10 | ```sh 11 | node ace 12 | ``` 13 | 14 | Since the `node` binary cannot run the TypeScript source code directly, we have to keep the ace file in pure JavaScript and use the `.js` extension. 15 | 16 | Under the hood, the `ace.js` file registers TS Node as an [ESM module loader hook](https://nodejs.org/api/module.html#customization-hooks) to execute the TypeScript code and imports the `bin/console.ts` file. 17 | 18 | ## Help and list commands 19 | 20 | You can view the list of available commands by running the ace entry point file without any arguments or using the `list` command. 21 | 22 | ```sh 23 | node ace 24 | 25 | # Same as above 26 | node ace list 27 | ``` 28 | 29 | ![](./ace_help_screen.jpeg) 30 | 31 | You can view help for a single command by typing the command name with the `--help` flag. 32 | 33 | ```sh 34 | node ace make:controller --help 35 | ``` 36 | 37 | :::note 38 | 39 | The output of the help screen is formatted as per the [docopt](http://docopt.org/) standard. 40 | 41 | ::: 42 | 43 | 44 | ## Enabling/disabling colors 45 | 46 | Ace detects the CLI environment in which it is running and disables the colorful output if the terminal does not support colors. However, you can manually enable or disable colors using the `--ansi` flag. 47 | 48 | ```sh 49 | # Disable colors 50 | node ace list --no-ansi 51 | 52 | # Force enable colors 53 | node ace list --ansi 54 | ``` 55 | 56 | ## Creating command aliases 57 | 58 | Command aliases provide a convenience layer to define aliases for commonly used commands. For example, if you often create singular resourceful controllers, you may create an alias for it inside the `adonisrc.ts` file. 59 | 60 | ```ts 61 | { 62 | commandsAliases: { 63 | resource: 'make:controller --resource --singular' 64 | } 65 | } 66 | ``` 67 | 68 | Once the alias is defined, you can use the alias to run the command. 69 | 70 | ```sh 71 | node ace resource admin 72 | ``` 73 | 74 | ### How alias expansion works? 75 | 76 | - Every time you run a command, Ace will check for aliases inside the `commandsAliases` object. 77 | - If an alias exists, the first segment (before the space) will be used to look up the command. 78 | - If a command exists, the rest of the alias value segments will be appended to the command name. 79 | 80 | For example, if you run the following command 81 | 82 | ```sh 83 | node ace resource admin --help 84 | ``` 85 | 86 | It will be expanded to 87 | 88 | ```sh 89 | make:controller --resource --singular admin --help 90 | ``` 91 | 92 | ## Running commands programmatically 93 | 94 | You can use the `ace` service to execute commands programmatically. The ace service is available after the application has been booted. 95 | 96 | The `ace.exec` method accepts the command name as the first parameter and an array of command line arguments as the second parameter. For example: 97 | 98 | ```ts 99 | import ace from '@adonisjs/core/services/ace' 100 | 101 | const command = await ace.exec('make:controller', [ 102 | 'user', 103 | '--resource', 104 | ]) 105 | 106 | console.log(command.exitCode) 107 | console.log(command.result) 108 | console.log(command.error) 109 | ``` 110 | 111 | You may use the `ace.hasCommand` method to check if a command exists before executing it. 112 | 113 | ```ts 114 | import ace from '@adonisjs/core/services/ace' 115 | 116 | /** 117 | * Boot method will load commands (if not already loaded) 118 | */ 119 | await ace.boot() 120 | 121 | if (ace.hasCommand('make:controller')) { 122 | await ace.exec('make:controller', [ 123 | 'user', 124 | '--resource', 125 | ]) 126 | } 127 | ``` 128 | -------------------------------------------------------------------------------- /content/docs/authentication/basic_auth_guard.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn how to use the basic auth guard to authenticate users using the HTTP authentication framework. 3 | --- 4 | 5 | # Basic authentication guard 6 | 7 | The basic auth guard is an implementation of the [HTTP authentication framework](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication), in which the client must pass the user credentials as a base64 encoded string via the `Authorization` header. The server allows the request if the credentials are valid. Otherwise, a web-native prompt is displayed to re-enter the credentials. 8 | 9 | ## Configuring the guard 10 | The authentication guards are defined inside the `config/auth.ts` file. You can configure multiple guards inside this file under the `guards` object. 11 | 12 | ```ts 13 | import { defineConfig } from '@adonisjs/auth' 14 | // highlight-start 15 | import { basicAuthGuard, basicAuthUserProvider } from '@adonisjs/auth/basic_auth' 16 | // highlight-end 17 | 18 | const authConfig = defineConfig({ 19 | default: 'basicAuth', 20 | guards: { 21 | // highlight-start 22 | basicAuth: basicAuthGuard({ 23 | provider: basicAuthUserProvider({ 24 | model: () => import('#models/user'), 25 | }), 26 | }) 27 | // highlight-end 28 | }, 29 | }) 30 | 31 | export default authConfig 32 | ``` 33 | 34 | The `basicAuthGuard` method creates an instance of the [BasicAuthGuard](https://github.com/adonisjs/auth/blob/main/modules/basic_auth_guard/guard.ts) class. It accepts a user provider that can be used to find users during authentication. 35 | 36 | The `basicAuthUserProvider` method creates an instance of the [BasicAuthLucidUserProvider](https://github.com/adonisjs/auth/blob/main/modules/basic_auth_guard/user_providers/lucid.ts) class. It accepts a reference to the model to use for verifying user credentials. 37 | 38 | 39 | ## Preparing the User model 40 | The model (`User` model in this example) configured with the `basicAuthUserProvider` must use the [AuthFinder](./verifying_user_credentials.md#using-the-auth-finder-mixin) mixin to verify the user credentials during authentication. 41 | 42 | ```ts 43 | import { DateTime } from 'luxon' 44 | import { compose } from '@adonisjs/core/helpers' 45 | import { BaseModel, column } from '@adonisjs/lucid/orm' 46 | // highlight-start 47 | import hash from '@adonisjs/core/services/hash' 48 | import { withAuthFinder } from '@adonisjs/auth/mixins/lucid' 49 | // highlight-end 50 | 51 | // highlight-start 52 | const AuthFinder = withAuthFinder(() => hash.use('scrypt'), { 53 | uids: ['email'], 54 | passwordColumnName: 'password', 55 | }) 56 | // highlight-end 57 | 58 | // highlight-start 59 | export default class User extends compose(BaseModel, AuthFinder) { 60 | // highlight-end 61 | @column({ isPrimary: true }) 62 | declare id: number 63 | 64 | @column() 65 | declare fullName: string | null 66 | 67 | @column() 68 | declare email: string 69 | 70 | @column() 71 | declare password: string 72 | 73 | @column.dateTime({ autoCreate: true }) 74 | declare createdAt: DateTime 75 | 76 | @column.dateTime({ autoCreate: true, autoUpdate: true }) 77 | declare updatedAt: DateTime 78 | } 79 | ``` 80 | 81 | ## Protecting routes 82 | Once you have configured the guard, you can use the `auth` middleware to protect routes from unauthenticated requests. The middleware is registered inside the `start/kernel.ts` file under the named middleware collection. 83 | 84 | ```ts 85 | import router from '@adonisjs/core/services/router' 86 | 87 | export const middleware = router.named({ 88 | auth: () => import('#middleware/auth_middleware') 89 | }) 90 | ``` 91 | 92 | ```ts 93 | // highlight-start 94 | import { middleware } from '#start/kernel' 95 | // highlight-end 96 | import router from '@adonisjs/core/services/router' 97 | 98 | router 99 | .get('dashboard', ({ auth }) => { 100 | return auth.user 101 | }) 102 | .use(middleware.auth({ 103 | // highlight-start 104 | guards: ['basicAuth'] 105 | // highlight-end 106 | })) 107 | ``` 108 | 109 | ### Handling authentication exception 110 | 111 | The auth middleware throws the [E_UNAUTHORIZED_ACCESS](https://github.com/adonisjs/auth/blob/main/src/errors.ts#L21) if the user is not authenticated. The exception is automatically converted to an HTTP response with the [WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) header in the response. The `WWW-Authenticate` challenges the authentication and triggers a web-native prompt to re-enter the credentials. 112 | 113 | ## Getting access to the authenticated user 114 | You may access the logged-in user instance using the `auth.user` property. Since, you are using the `auth` middleware, the `auth.user` property will always be available. 115 | 116 | ```ts 117 | import { middleware } from '#start/kernel' 118 | import router from '@adonisjs/core/services/router' 119 | 120 | router 121 | .get('dashboard', ({ auth }) => { 122 | return `You are authenticated as ${auth.user!.email}` 123 | }) 124 | .use(middleware.auth({ 125 | guards: ['basicAuth'] 126 | })) 127 | ``` 128 | 129 | ### Get authenticated user or fail 130 | If you do not like using the [non-null assertion operator](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#non-null-assertion-operator-postfix-) on the `auth.user` property, you may use the `auth.getUserOrFail` method. This method will return the user object or throw [E_UNAUTHORIZED_ACCESS](../references/exceptions.md#e_unauthorized_access) exception. 131 | 132 | ```ts 133 | import { middleware } from '#start/kernel' 134 | import router from '@adonisjs/core/services/router' 135 | 136 | router 137 | .get('dashboard', ({ auth }) => { 138 | // highlight-start 139 | const user = auth.getUserOrFail() 140 | return `You are authenticated as ${user.email}` 141 | // highlight-end 142 | }) 143 | .use(middleware.auth({ 144 | guards: ['basicAuth'] 145 | })) 146 | ``` 147 | -------------------------------------------------------------------------------- /content/docs/basics/attach-debug.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/attach-debug.mp4 -------------------------------------------------------------------------------- /content/docs/basics/browser-dd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/browser-dd.png -------------------------------------------------------------------------------- /content/docs/basics/cookies.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn how to read, write, and clear cookies in AdonisJS. 3 | --- 4 | 5 | # Cookies 6 | 7 | The request cookies are parsed automatically during an HTTP request. You can read cookies using the [request](./request.md) object and set/clear cookies using the [response](./response.md) object. 8 | 9 | ```ts 10 | // title: Read cookies 11 | import router from '@adonisjs/core/services/router' 12 | 13 | router.get('cart', async ({ request }) => { 14 | // highlight-start 15 | const cartItems = request.cookie('cart_items', []) 16 | // highlight-end 17 | console.log(cartItems) 18 | }) 19 | ``` 20 | 21 | ```ts 22 | // title: Set cookies 23 | import router from '@adonisjs/core/services/router' 24 | 25 | router.post('cart', async ({ request, response }) => { 26 | const id = request.input('product_id') 27 | // highlight-start 28 | response.cookie('cart_items', [{ id }]) 29 | // highlight-end 30 | }) 31 | ``` 32 | 33 | ```ts 34 | // title: Clear cookies 35 | import router from '@adonisjs/core/services/router' 36 | 37 | router.delete('cart', async ({ request, response }) => { 38 | // highlight-start 39 | response.clearCookie('cart_items') 40 | // highlight-end 41 | }) 42 | ``` 43 | 44 | ## Configuration 45 | 46 | The default configuration for setting cookies is defined inside the `config/app.ts` file. Feel free to tweak the options as per your application requirements. 47 | 48 | ```ts 49 | http: { 50 | cookie: { 51 | domain: '', 52 | path: '/', 53 | maxAge: '2h', 54 | httpOnly: true, 55 | secure: true, 56 | sameSite: 'lax', 57 | /** 58 | * Experimental properties 59 | * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#partitioned 60 | */ 61 | partitioned: false, 62 | priority: 'medium', 63 | } 64 | } 65 | ``` 66 | 67 | The options are converted to [set-cookie attributes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes). The `maxAge` property accepts a string-based time expression, and its value will be converted to seconds. 68 | 69 | The same set of options can be overridden when setting the cookies. 70 | 71 | ```ts 72 | response.cookie('key', value, { 73 | domain: '', 74 | path: '/', 75 | maxAge: '2h', 76 | httpOnly: true, 77 | secure: true, 78 | sameSite: 'lax', 79 | }) 80 | ``` 81 | 82 | ## Supported data types 83 | 84 | The cookie values are serialized to a string using `JSON.stringify`; therefore, you can use the following JavaScript data types as cookie values. 85 | 86 | - string 87 | - number 88 | - bigInt 89 | - boolean 90 | - null 91 | - object 92 | - array 93 | 94 | ```ts 95 | // Object 96 | response.cookie('user', { 97 | id: 1, 98 | fullName: 'virk', 99 | }) 100 | 101 | // Array 102 | response.cookie('product_ids', [1, 2, 3, 4]) 103 | 104 | // Boolean 105 | response.cookie('is_logged_in', true) 106 | 107 | // Number 108 | response.cookie('visits', 10) 109 | 110 | // BigInt 111 | response.cookie('visits', BigInt(10)) 112 | 113 | // Data objects are converted to ISO string 114 | response.cookie('visits', new Date()) 115 | ``` 116 | 117 | ## Signed cookies 118 | 119 | The cookies set using the `response.cookie` method are signed. A signed cookie is tamper-proof, meaning changing its contents will invalidate its signature, and the cookie will be ignored. 120 | 121 | The cookies are signed using the `appKey` defined inside the `config/app.ts` file. 122 | 123 | 124 | :::note 125 | 126 | The signed cookies are still readable by Base64 decoding them. You can use encrypted cookies if you want the cookie value to be unreadable. 127 | 128 | 129 | ::: 130 | 131 | 132 | ```ts 133 | import router from '@adonisjs/core/services/router' 134 | 135 | router.get('/', async ({ request, response }) => { 136 | // set signed cookie 137 | response.cookie('user_id', 1) 138 | 139 | // read signed cookie 140 | request.cookie('user_id') 141 | }) 142 | ``` 143 | 144 | ## Encrypted cookies 145 | 146 | Unlike signed cookies, the encrypted cookie value cannot be decoded to plain text. Therefore, you can use encrypted cookies for values containing sensitive information that should not be readable by anyone. 147 | 148 | Encrypted cookies are set using the `response.encryptedCookie` method and read using the `request.encryptedCookie` method. Under the hood, these methods use the [Encryption module](../security/encryption.md). 149 | 150 | ```ts 151 | import router from '@adonisjs/core/services/router' 152 | 153 | router.get('/', async ({ request, response }) => { 154 | // set encrypted cookie 155 | response.encryptedCookie('user_id', 1) 156 | 157 | // read encrypted cookie 158 | request.encryptedCookie('user_id') 159 | }) 160 | ``` 161 | 162 | ## Plain cookies 163 | 164 | Plain cookies are meant to be used when you want the cookie to be accessed by your frontend code using the `document.cookie` value. 165 | 166 | By default, we call `JSON.stringify` on raw values and convert them to a base64 encoded string. It is done so that you can use `objects` and `arrays` for the cookie value. However, the encoding can be turned off. 167 | 168 | Plain cookies are defined using the `response.plainCookie` method and can be read using the `request.plainCookie` method. 169 | 170 | ```ts 171 | import router from '@adonisjs/core/services/router' 172 | 173 | router.get('/', async ({ request, response }) => { 174 | // set plain cookie 175 | response.plainCookie('user', { id: 1 }, { 176 | httpOnly: true 177 | }) 178 | 179 | // read plain cookie 180 | request.plainCookie('user') 181 | }) 182 | ``` 183 | 184 | To turn off encoding, set `encoding: false` in the options object. 185 | 186 | ```ts 187 | response.plainCookie('token', tokenValue, { 188 | httpOnly: true, 189 | encode: false, 190 | }) 191 | 192 | // Read plain cookie with encoding off 193 | request.plainCookie('token', { 194 | encoded: false 195 | }) 196 | ``` 197 | 198 | ## Setting cookies during tests 199 | The following guides cover the usage of cookies when writing tests. 200 | 201 | - Defining cookies with [Japa API client](../testing/http_tests.md#readingwriting-cookies). 202 | - Defining cookie with [Japa browser client](../testing/browser_tests.md#readingwriting-cookies). 203 | -------------------------------------------------------------------------------- /content/docs/basics/dd-cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/dd-cli.png -------------------------------------------------------------------------------- /content/docs/basics/dd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/dd.png -------------------------------------------------------------------------------- /content/docs/basics/dd_browser_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/dd_browser_output.png -------------------------------------------------------------------------------- /content/docs/basics/dd_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/dd_original.png -------------------------------------------------------------------------------- /content/docs/basics/debug-dev-server-original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/debug-dev-server-original.png -------------------------------------------------------------------------------- /content/docs/basics/debug-dev-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/debug-dev-server.png -------------------------------------------------------------------------------- /content/docs/basics/debug-sql-queries-original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/debug-sql-queries-original.png -------------------------------------------------------------------------------- /content/docs/basics/debug-sql-queries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/debug-sql-queries.png -------------------------------------------------------------------------------- /content/docs/basics/middleware_flow 2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/middleware_flow 2.jpeg -------------------------------------------------------------------------------- /content/docs/basics/middleware_flow.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/middleware_flow.jpeg -------------------------------------------------------------------------------- /content/docs/basics/post_comments_resource_routes_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/post_comments_resource_routes_list.png -------------------------------------------------------------------------------- /content/docs/basics/post_comments_resource_routes_list_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/post_comments_resource_routes_list_original.png -------------------------------------------------------------------------------- /content/docs/basics/post_resource_routes_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/post_resource_routes_list.png -------------------------------------------------------------------------------- /content/docs/basics/post_resource_routes_list_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/post_resource_routes_list_original.png -------------------------------------------------------------------------------- /content/docs/basics/shallow_routes_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/shallow_routes_list.png -------------------------------------------------------------------------------- /content/docs/basics/shallow_routes_list_original.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/shallow_routes_list_original.jpg -------------------------------------------------------------------------------- /content/docs/basics/static_file_server.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Serve static files from a given directory using the @adonisjs/static package. 3 | --- 4 | 5 | # Static files server 6 | 7 | You can serve static files from a given directory using the `@adonisjs/static` package. The package ships with a middleware that you must register in the [server middleware stack](./middleware.md#server-middleware-stack) to intercept the HTTP requests and serve files. 8 | 9 | ## Installation 10 | 11 | The package comes pre-configured with the `web` starter kit. However, you can install and configure it as follows with other starter kits. 12 | 13 | 14 | Install and configure the package using the following command : 15 | 16 | ```sh 17 | node ace add @adonisjs/static 18 | ``` 19 | 20 | :::disclosure{title="See steps performed by the add command"} 21 | 22 | 1. Installs the `@adonisjs/static` package using the detected package manager. 23 | 24 | 2. Registers the following service provider inside the `adonisrc.ts` file. 25 | 26 | ```ts 27 | { 28 | providers: [ 29 | // ...other providers 30 | () => import('@adonisjs/static/static_provider') 31 | ] 32 | } 33 | ``` 34 | 35 | 3. Create the `config/static.ts` file. 36 | 37 | 4. Registers the following middleware inside the `start/kernel.ts` file. 38 | 39 | ```ts 40 | server.use([ 41 | () => import('@adonisjs/static/static_middleware') 42 | ]) 43 | ``` 44 | 45 | ::: 46 | 47 | ## Configuration 48 | 49 | The configuration for the static middleware is stored inside the `config/static.ts` file. 50 | 51 | ```ts 52 | import { defineConfig } from '@adonisjs/static' 53 | 54 | const staticServerConfig = defineConfig({ 55 | enabled: true, 56 | etag: true, 57 | lastModified: true, 58 | dotFiles: 'ignore', 59 | }) 60 | 61 | export default staticServerConfig 62 | ``` 63 | 64 |
65 | 66 |
67 | 68 | enabled 69 | 70 |
71 | 72 |
73 | 74 | Enable or disable the middleware temporarily without removing it from the middleware stack. 75 | 76 |
77 | 78 |
79 | 80 | acceptRanges 81 | 82 |
83 | 84 |
85 | 86 | The `Accept-Range` header allows browsers to resume an interrupted file download instead of trying to restart the download. You can disable resumable downloads by setting `acceptsRanges` to `false`. 87 | 88 | Defaults to `true`. 89 | 90 |
91 | 92 |
93 | 94 | cacheControl 95 | 96 |
97 | 98 |
99 | 100 | Enable or disable the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) header. The `immutable` and `maxAge` properties will be ignored when `cacheControl` is disabled. 101 | 102 | 103 | ```ts 104 | { 105 | cacheControl: true 106 | } 107 | ``` 108 |
109 | 110 | 111 |
112 | 113 | dotFiles 114 | 115 |
116 | 117 |
118 | 119 | Define how to treat requests for dot files inside the `public` directory. You can set one of the following options. 120 | 121 | - `allow`: Serve the dot-file same as the other files. 122 | - `deny`: Deny the request with the `403` status code. 123 | - `ignore`: Pretend the file does not exist and respond with a `404` status code. 124 | 125 | ```ts 126 | { 127 | dotFiles: 'ignore' 128 | } 129 | ``` 130 | 131 |
132 | 133 | 134 |
135 | 136 | etag 137 | 138 |
139 | 140 |
141 | 142 | 143 | Enable or disable [etag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) generation. 144 | 145 | ```ts 146 | { 147 | etag: true, 148 | } 149 | ``` 150 | 151 |
152 | 153 |
154 | 155 | lastModified 156 | 157 |
158 | 159 |
160 | 161 | 162 | Enable or disable the [Last-Modified](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified) header. The file [stat.mtime](https://nodejs.org/api/fs.html#statsmtime) property is used as the value for the header. 163 | 164 | ```ts 165 | { 166 | lastModified: true, 167 | } 168 | ``` 169 | 170 |
171 | 172 | 173 |
174 | 175 | immutable 176 | 177 |
178 | 179 |
180 | 181 | 182 | Enable or disable the [immutable](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#immutable) directive for the `Cache-Control` header. By default, the `immutable` property is disabled. 183 | 184 | If the `immutable` property is enabled, you must define the `maxAge` property to enable caching. 185 | 186 | ```ts 187 | { 188 | immutable: true 189 | } 190 | ``` 191 | 192 |
193 | 194 |
195 | 196 | maxAge 197 | 198 |
199 | 200 |
201 | 202 | Define the [max-age](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#max-age) directive for the `Cache-Control` header. The value should be either in milliseconds or a time expression string. 203 | 204 | ```ts 205 | { 206 | maxAge: '30 mins' 207 | } 208 | ``` 209 | 210 |
211 | 212 |
213 | 214 | headers 215 | 216 |
217 | 218 |
219 | 220 | A function that returns an object of headers to set on the response. The function receives the file path as the first argument and the [file stats](https://nodejs.org/api/fs.html#class-fsstats) object as the second argument. 221 | 222 | ```ts 223 | { 224 | headers: (path, stats) => { 225 | if (path.endsWith('.mc2')) { 226 | return { 227 | 'content-type': 'application/octet-stream' 228 | } 229 | } 230 | } 231 | } 232 | ``` 233 | 234 |
235 | 236 | 237 |
238 | 239 | ## Serving static files 240 | 241 | Once the middleware is registered, you may create files inside the `public` directory and access them in the browser using the file path. For example, the `./public/css/style.css` file can be accessed using the `http://localhost:3333/css/style.css` URL. 242 | 243 | The files in the `public` directory are not compiled or built using an assets bundler. If you want to compile frontend assets, you must place them inside the `resources` directory and use the [assets bundler](../basics/vite.md). 244 | 245 | ## Copying static files to production build 246 | The static files stored inside the `/public` directory are automatically copied to the `build` folder when you run `node ace build` command. 247 | 248 | The rule for copying public files is defined inside the `adonisrc.ts` file. 249 | 250 | ```ts 251 | { 252 | metaFiles: [ 253 | { 254 | pattern: 'public/**', 255 | reloadServer: false 256 | } 257 | ] 258 | } 259 | ``` 260 | -------------------------------------------------------------------------------- /content/docs/basics/vscode_routes_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/vscode_routes_list.png -------------------------------------------------------------------------------- /content/docs/basics/vscode_routes_list_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/basics/vscode_routes_list_original.png -------------------------------------------------------------------------------- /content/docs/concepts/assembler_hooks.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Assembler hooks are a way of executing code at specific points in the assembler lifecycle. 3 | --- 4 | 5 | # Assembler hooks 6 | 7 | Assembler hooks are a way of executing code at specific points in the assembler lifecycle. As a reminder, the Assembler is a part of AdonisJS that enables you to launch your dev server, build your application, and run your tests. 8 | 9 | These hooks can be helpful for tasks such as file generation, code compilation, or injecting custom build steps. 10 | 11 | For example, the `@adonisjs/vite` package uses the `onBuildStarting` hook to inject a step where front-end assets are built. So, when you run `node ace build`, the `@adonisjs/vite` package will build your front-end assets before the rest of the build process. This is a good example of how hooks can be used to customize the build process. 12 | 13 | ## Adding a hook 14 | 15 | Assembler hooks are defined in the `adonisrc.ts` file, in the `hooks` key : 16 | 17 | ```ts 18 | import { defineConfig } from '@adonisjs/core/app' 19 | 20 | export default defineConfig({ 21 | hooks: { 22 | onBuildCompleted: [ 23 | () => import('my-package/hooks/on_build_completed') 24 | ], 25 | onBuildStarting: [ 26 | () => import('my-package/hooks/on_build_starting') 27 | ], 28 | onDevServerStarted: [ 29 | () => import('my-package/hooks/on_dev_server_started') 30 | ], 31 | onSourceFileChanged: [ 32 | () => import('my-package/hooks/on_source_file_changed') 33 | ], 34 | }, 35 | }) 36 | ``` 37 | 38 | Several hooks can be defined for each stage of the assembly lifecycle. Each hook is an array of functions to be executed. 39 | 40 | We recommend using dynamic imports to load hooks. It ensures that hooks are not loaded unnecessarily but only when needed. If you write your hook code directly in the `adonisrc.ts` file, this may slow down the start-up of your application. 41 | 42 | ## Create a hook 43 | 44 | A hook is just a simple function. Let's take an example of a hook that is supposed to execute a custom build task. 45 | 46 | ```ts 47 | // title: hooks/on_build_starting.ts 48 | import type { AssemblerHookHandler } from '@adonisjs/core/types/app' 49 | 50 | const buildHook: AssemblerHookHandler = async ({ logger }) => { 51 | logger.info('Generating some files...') 52 | 53 | await myCustomLogic() 54 | } 55 | 56 | export default buildHook 57 | ``` 58 | 59 | Note that the hook must be exported by default. 60 | 61 | Once this hook has been defined, all you have to do is add it to the `adonisrc.ts` file like this: 62 | 63 | ```ts 64 | // title: adonisrc.ts 65 | import { defineConfig } from '@adonisjs/core/app' 66 | 67 | export default defineConfig({ 68 | hooks: { 69 | onBuildStarting: [ 70 | () => import('./hooks/on_build_starting') 71 | ], 72 | }, 73 | }) 74 | ``` 75 | 76 | And now, every time you run `node ace build`, the `onBuildStarting` hook will be executed with the custom logic you defined. 77 | 78 | ## Hooks list 79 | 80 | Here's the list of available hooks: 81 | 82 | ### onBuildStarting 83 | 84 | This hook is executed before the build starts. It is helpful for tasks such as file generation or for injecting custom build steps. 85 | 86 | ### onBuildCompleted 87 | 88 | This hook is executed once the build is complete. It can also be used to customize the build process. 89 | 90 | ### onDevServerStarted 91 | 92 | This hook is executed once the Adonis dev server is started. 93 | 94 | ### onSourceFileChanged 95 | 96 | This hook is executed each time a source file (included by your `tsconfig.json` ) is modified. Your hook will receive the path of the modified file as an argument. 97 | -------------------------------------------------------------------------------- /content/docs/concepts/boot_phase_flow_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/boot_phase_flow_chart.png -------------------------------------------------------------------------------- /content/docs/concepts/boot_phase_flow_chart_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/boot_phase_flow_chart_original.png -------------------------------------------------------------------------------- /content/docs/concepts/extending_the_framework.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn how to extend the AdonisJS framework using macros and getters. 3 | --- 4 | 5 | # Extending the framework 6 | 7 | The architecture of AdonisJS makes it very easy to extend the framework. We dogfood framework's core APIs to build an ecosystem of first-party packages. 8 | 9 | In this guide, we will explore different APIs you can use to extend the framework through a package or within your application codebase. 10 | 11 | ## Macros and getters 12 | 13 | Macros and getters offer an API to add properties to the prototype of a class. You can think of them as Syntactic sugar for `Object.defineProperty`. Under the hood, we use [macroable](https://github.com/poppinss/macroable) package, and you can refer to its README for an in-depth technical explanation. 14 | 15 | Since macros and getters are added at runtime, you will have to inform TypeScript about the type information for the added property using [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html). 16 | 17 | You can write the code for adding macros inside a dedicated file (like the `extensions.ts`) and import it inside the service provider's `boot` method. 18 | 19 | ```ts 20 | // title: providers/app_provider.ts 21 | export default class AppProvider { 22 | async boot() { 23 | await import('../src/extensions.js') 24 | } 25 | } 26 | ``` 27 | 28 | In the following example, we add the `wantsJSON` method to the [Request](../basics/request.md) class and define its types simultaneously. 29 | 30 | ```ts 31 | // title: src/extensions.ts 32 | import { Request } from '@adonisjs/core/http' 33 | 34 | Request.macro('wantsJSON', function (this: Request) { 35 | const firstType = this.types()[0] 36 | if (!firstType) { 37 | return false 38 | } 39 | 40 | return firstType.includes('/json') || firstType.includes('+json') 41 | }) 42 | ``` 43 | 44 | ```ts 45 | // title: src/extensions.ts 46 | declare module '@adonisjs/core/http' { 47 | interface Request { 48 | wantsJSON(): boolean 49 | } 50 | } 51 | ``` 52 | 53 | - The module path during the `declare module` call must be the same as the path you use to import the class. 54 | - The `interface` name must be the same as the class name to which you add the macro or the getter. 55 | 56 | ### Getters 57 | 58 | Getters are lazily evaluated properties added to a class. You can add a getter using the `Class.getter` method. The first argument is the getter name, and the second argument is the callback function to compute the property value. 59 | 60 | Getter callbacks cannot be async because [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) in JavaScript cannot be asynchronous. 61 | 62 | ```ts 63 | import { Request } from '@adonisjs/core/http' 64 | 65 | Request.getter('hasRequestId', function (this: Request) { 66 | return this.header('x-request-id') 67 | }) 68 | 69 | // you can use the property as follows. 70 | if (ctx.request.hasRequestId) { 71 | } 72 | ``` 73 | 74 | Getters can be a singleton, meaning the function to compute the getter value will be called once, and the return value will be cached for an instance of the class. 75 | 76 | ```ts 77 | const isSingleton = true 78 | 79 | Request.getter('hasRequestId', function (this: Request) { 80 | return this.header('x-request-id') 81 | }, isSingleton) 82 | ``` 83 | 84 | ### Macroable classes 85 | 86 | Following is the list of classes that can be extended using Macros and getters. 87 | 88 | | Class | Import path | 89 | |------------------------------------------------------------------------------------------------|-----------------------------| 90 | | [Application](https://github.com/adonisjs/application/blob/main/src/application.ts) | `@adonisjs/core/app` | 91 | | [Request](https://github.com/adonisjs/http-server/blob/main/src/request.ts) | `@adonisjs/core/http` | 92 | | [Response](https://github.com/adonisjs/http-server/blob/main/src/response.ts) | `@adonisjs/core/http` | 93 | | [HttpContext](https://github.com/adonisjs/http-server/blob/main/src/http_context/main.ts) | `@adonisjs/core/http` | 94 | | [Route](https://github.com/adonisjs/http-server/blob/main/src/router/route.ts) | `@adonisjs/core/http` | 95 | | [RouteGroup](https://github.com/adonisjs/http-server/blob/main/src/router/group.ts) | `@adonisjs/core/http` | 96 | | [RouteResource](https://github.com/adonisjs/http-server/blob/main/src/router/resource.ts) | `@adonisjs/core/http` | 97 | | [BriskRoute](https://github.com/adonisjs/http-server/blob/main/src/router/brisk.ts) | `@adonisjs/core/http` | 98 | | [ExceptionHandler](https://github.com/adonisjs/http-server/blob/main/src/exception_handler.ts) | `@adonisjs/core/http` | 99 | | [MultipartFile](https://github.com/adonisjs/bodyparser/blob/main/src/multipart/file.ts) | `@adonisjs/core/bodyparser` | 100 | 101 | 102 | ## Extending modules 103 | Most of the AdonisJS modules provide extensible APIs to register custom implementations. Following is an aggregated list of the same. 104 | 105 | - [Creating Hash driver](../security/hashing.md#creating-a-custom-hash-driver) 106 | - [Creating Session driver](../basics/session.md#creating-a-custom-session-store) 107 | - [Creating Social auth driver](../authentication/social_authentication.md#creating-a-custom-social-driver) 108 | - [Extending REPL](../digging_deeper/repl.md#adding-custom-methods-to-repl) 109 | - [Creating i18n translations loader](../digging_deeper/i18n.md#creating-a-custom-translation-loader) 110 | - [Creating i18n translations formatter](../digging_deeper/i18n.md#creating-a-custom-translation-formatter) 111 | -------------------------------------------------------------------------------- /content/docs/concepts/hmr.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Update your AdonisJS application without restarting the process using hot module replacement (HMR). 3 | --- 4 | 5 | # Hot module replacement 6 | 7 | Hot module replacement (HMR) refers to the process of reloading JavaScript modules after modification without restarting the entire process. HMR usually results in a faster feedback loop since, after a file change, you do not have to wait for the whole of the process to restart. 8 | 9 | The term HMR has been used for many years now in the frontend ecosystem, where tools like Vite can hot-reload modules and apply changes to a webpage while maintaining its existing state. 10 | 11 | However, the HMR performed by AdonisJS is a lot simpler and vastly differs from tools like Vite or Webpack. Our goal with HMR is to offer faster reloads, and that's it. 12 | 13 | ## Key concepts 14 | 15 | ### No updates are propagated to the browser 16 | 17 | Since AdonisJS is a backend framework, we are not in charge of maintaining the state of a frontend application or applying CSS to a web page. Therefore, our HMR integration cannot talk to your frontend app and reconcile its state. 18 | 19 | In fact, not every AdonisJS application is a browser-rendered web app. Many use AdonisJS for creating pure JSON APIs, and they can also benefit from our HMR integration. 20 | 21 | ### Works only with dynamic imports 22 | Most HMR tools use code transformations to inject additional code into the compiled output. At AdonisJS, we are not a big fan of transpilers and always strive to embrace the platform as it is. Therefore, our approach to HMR uses [Node.js loader hooks](https://nodejs.org/api/module.html#customization-hooks) and works only with dynamic imports. 23 | 24 | **The good news is that all the critical parts of your AdonisJS application are dynamically imported by default**. For example, Controllers, middleware, and event listeners are all dynamically imported, and hence, you can leverage HMR from today without changing a single line of code in your app. 25 | 26 | It is worth mentioning that the imports of a dynamically imported module can be at the top level. For example, a controller (which is dynamically imported in the routes file) can have top-level imports for validators, TSX files, models, and services, and they all benefit from HMR. 27 | 28 | ## Usage 29 | All official starter kits have been updated to use HMR by default. However, if you have an existing application, you can configure HMR as follows. 30 | 31 | Install the [hot-hook](https://github.com/Julien-R44/hot-hook) npm package as a development dependency. The AdonisJS core team has created this package, which can also be used outside of an AdonisJS application. 32 | 33 | ```sh 34 | npm i -D hot-hook 35 | ``` 36 | 37 | Next, copy-paste the following configuration to the `package.json` file. The `boundaries` property accepts an array of glob patterns that must be considered for HMR. 38 | 39 | ```json 40 | { 41 | "hotHook": { 42 | "boundaries": [ 43 | "./app/controllers/**/*.ts", 44 | "./app/middleware/*.ts" 45 | ] 46 | } 47 | } 48 | ``` 49 | 50 | After the configuration, you can start the development server with the `--hmr` flag. 51 | 52 | ```sh 53 | node ace serve --hmr 54 | ``` 55 | 56 | Also, you might want to update the `dev` script within the `package.json` file to use this new flag. 57 | 58 | ```json 59 | { 60 | "scripts": { 61 | "dev": "node ace serve --hmr" 62 | } 63 | } 64 | ``` 65 | 66 | ## Full reloads vs HMR 67 | 68 | :::note 69 | 70 | This section explains the underlying workings of `hot-hook`. Feel free to skip it if you are not in the mood to read extended technical theory 🤓 71 | 72 | Or, go through the [README file](https://github.com/Julien-R44/hot-hook) of the package if you want an even deeper explanation. 73 | 74 | ::: 75 | 76 | Let's understand when AdonisJS will perform a complete reload (restarting the process) and when it will hot reload the module. 77 | 78 | ### Creating a dependency tree 79 | When using the `--hmr` flag, AdonisJS will use `hot-hook` to create a dependency tree of your application starting from the `bin/server.ts` file and will watch all the files that are part of this dependency tree. 80 | 81 | It means that if you create a TypeScript file in your application source code but never import it anywhere in your app, this file will not trigger any reload. It will be ignored as if the file does not exist. 82 | 83 | ### Identifying boundaries 84 | Next, `hot-hook` will use the `boundaries` array from the configuration to identify the files that qualify for HMR. 85 | 86 | As a rule of thumb, you should never register config files, service providers, or preload files as boundaries. This is because these files usually result in some side-effect that will re-occur if we reload them without clearing the side-effects. Here are some examples: 87 | 88 | - The `config/database.ts` file establishes a connection with the database. Hot reloading this file means closing the existing connection and re-creating it. The same can be achieved by restarting the entire process without adding any additional complexity. 89 | 90 | - The `start/routes.ts` file is used to register the routes. Hot reloading this file means removing existing routes registered with the framework and re-registering them. Again, restarting the process is simple. 91 | 92 | In other words, we can say that the modules imported/executed during an HTTP request should be part of HMR boundaries, and modules needed to boot the application should not be. 93 | 94 | ### Performing reloads 95 | Once `hot-hook` has identified the boundaries, it will perform HMR for dynamically imported modules that are part of the boundary and restart the process for the rest of the files. 96 | 97 | -------------------------------------------------------------------------------- /content/docs/concepts/scaffolding_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/scaffolding_workflow.png -------------------------------------------------------------------------------- /content/docs/concepts/scaffolding_workflow_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/scaffolding_workflow_original.png -------------------------------------------------------------------------------- /content/docs/concepts/server_boot_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/server_boot_lifecycle.png -------------------------------------------------------------------------------- /content/docs/concepts/server_boot_lifecycle_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/server_boot_lifecycle_original.png -------------------------------------------------------------------------------- /content/docs/concepts/service_providers.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Service providers are plain JavaScript classes with lifecycle methods to perform actions during different phases of the application. 3 | --- 4 | 5 | # Service providers 6 | 7 | Services providers are plain JavaScript classes with lifecycle methods to perform actions during different phases of the application. 8 | 9 | A service provider can register [bindings into the container](../concepts/dependency_injection.md#container-bindings), [extend existing bindings](../concepts/dependency_injection.md#container-events), or run actions after the HTTP server starts. 10 | 11 | Service providers are the entry point to an AdonisJS application with the ability to modify the application state before it is considered ready. **It is mainly used by external packages to hook into the application lifecycle**. 12 | 13 | :::note 14 | If you only want to inject dependencies into one of your classes, you can use the [dependency injection](../concepts/dependency_injection.md) feature. 15 | ::: 16 | 17 | The providers are registered inside the `adonisrc.ts` file under the `providers` array. The value is a function to lazily import the service provider 18 | 19 | ```ts 20 | { 21 | providers: [ 22 | () => import('@adonisjs/core/providers/app_provider'), 23 | () => import('./providers/app_provider.js'), 24 | ] 25 | } 26 | ``` 27 | 28 | By default, a provider is loaded in all the runtime environments. However, you can limit the provider to run in specific environments. 29 | 30 | ```ts 31 | { 32 | providers: [ 33 | () => import('@adonisjs/core/providers/app_provider'), 34 | { 35 | file: () => import('./providers/app_provider.js'), 36 | environment: ['web', 'repl'] 37 | } 38 | ] 39 | } 40 | ``` 41 | 42 | ## Writing service providers 43 | 44 | Service providers are stored inside the `providers` directory of your app. Alternatively, you can use the `node ace make:provider app` command. 45 | 46 | The provider module must have an `export default` statement returning the provider class. The class constructor receives an instance of the [Application](./application.md) class. 47 | 48 | See also: [Make provider command](../references/commands.md#makeprovider) 49 | 50 | ```ts 51 | import { ApplicationService } from '@adonisjs/core/types' 52 | 53 | export default class AppProvider { 54 | constructor(protected app: ApplicationService) { 55 | } 56 | } 57 | ``` 58 | 59 | Following are the lifecycle methods you can implement to perform different actions. 60 | 61 | ```ts 62 | export default class AppProvider { 63 | register() { 64 | } 65 | 66 | async boot() { 67 | } 68 | 69 | async start() { 70 | } 71 | 72 | async ready() { 73 | } 74 | 75 | async shutdown() { 76 | } 77 | } 78 | ``` 79 | 80 | ### register 81 | 82 | The `register` method is called after an instance of the provider class is created. The `register` method can register bindings within the IoC container. 83 | 84 | The `register` method is synchronous, so you cannot use Promises inside this method. 85 | 86 | ```ts 87 | export default class AppProvider { 88 | register() { 89 | this.app.container.bind('db', () => { 90 | return new Database() 91 | }) 92 | } 93 | } 94 | ``` 95 | 96 | ### boot 97 | 98 | The `boot` method is called after all the bindings have been registered with the IoC container. Inside this method, you can resolve bindings from the container to extend/mutate them. 99 | 100 | ```ts 101 | export default class AppProvider { 102 | async boot() { 103 | const validator = await this.app.container.make('validator') 104 | 105 | // Add custom validation rules 106 | validator.rule('foo', () => {}) 107 | } 108 | } 109 | ``` 110 | 111 | It is a good practice to extend bindings when they are resolved from the container. For example, you can use the `resolving` hook to add custom rules to the validator. 112 | 113 | ```ts 114 | async boot() { 115 | this.app.container.resolving('validator', (validator) => { 116 | validator.rule('foo', () => {}) 117 | }) 118 | } 119 | ``` 120 | 121 | ### start 122 | 123 | The `start` method is called after the `boot` and before the `ready ` method. It allows you to perform actions that the `ready` hook actions might need. 124 | 125 | ### ready 126 | 127 | The `ready` method gets called at different stages based on the application's environment. 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |
web The ready method is called after the HTTP server has been started and is ready to accept requests.
consoleThe ready method is called just before the run method of the main command.
testThe ready method is called just before running all the tests. However, the test files are imported before the ready method.
replThe ready method is called before the REPL prompt is displayed on the terminal.
147 | 148 | ```ts 149 | export default class AppProvider { 150 | async start() { 151 | if (this.app.getEnvironment() === 'web') { 152 | } 153 | 154 | if (this.app.getEnvironment() === 'console') { 155 | } 156 | 157 | if (this.app.getEnvironment() === 'test') { 158 | } 159 | 160 | if (this.app.getEnvironment() === 'repl') { 161 | } 162 | } 163 | } 164 | ``` 165 | 166 | ### shutdown 167 | 168 | The `shutdown` method is called when AdonisJS is in the middle of gracefully exiting the application. 169 | 170 | The event of exiting the application depends upon the environment in which the app is running and how the application process started. Please read the [application lifecycle guide](./application_lifecycle.md) to know more about it. 171 | 172 | ```ts 173 | export default class AppProvider { 174 | async shutdown() { 175 | // perform the cleanup 176 | } 177 | } 178 | ``` 179 | -------------------------------------------------------------------------------- /content/docs/concepts/start_phase_flow_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/start_phase_flow_chart.png -------------------------------------------------------------------------------- /content/docs/concepts/start_phase_flow_chart_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/start_phase_flow_chart_original.png -------------------------------------------------------------------------------- /content/docs/concepts/termination_phase_flow_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/termination_phase_flow_chart.png -------------------------------------------------------------------------------- /content/docs/concepts/termination_phase_flow_chart_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/concepts/termination_phase_flow_chart_original.png -------------------------------------------------------------------------------- /content/docs/concepts/tooling_config.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn about the tooling configuration presets used by AdonisJS for TypeScript, Prettier, and ESLint. 3 | --- 4 | 5 | # Tooling config 6 | 7 | AdonisJS relies heavily on TypeScript, Prettier, and ESLint to have consistency in code, check for errors at build time, and more importantly, have a joyful development experience. 8 | 9 | We have abstracted all our choices inside ready-to-use configuration presets used by all the official packages and by the official starter kits. 10 | 11 | Continue reading this guide if you want to use the same configuration presets in your Node.js applications written in TypeScript. 12 | 13 | ## TSConfig 14 | 15 | The [`@adonisjs/tsconfig`](https://github.com/adonisjs/tooling-config/tree/main/packages/typescript-config) package contains the base configuration for TypeScript projects. We set the TypeScript module system to `NodeNext` and use `TS Node + SWC` for Just-in-Time compilation. 16 | 17 | Feel free to explore options inside the [base config file](https://github.com/adonisjs/tooling-config/blob/main/packages/typescript-config/tsconfig.base.json), [application config file](https://github.com/adonisjs/tooling-config/blob/main/packages/typescript-config/tsconfig.app.json), and [package development config file](https://github.com/adonisjs/tooling-config/blob/main/packages/typescript-config/tsconfig.package.json). 18 | 19 | You can install the package and use it as follows. 20 | 21 | ```sh 22 | npm i -D @adonisjs/tsconfig 23 | 24 | # Make sure also to install the following packages 25 | npm i -D typescript ts-node-maintained @swc/core 26 | ``` 27 | 28 | Extend from the `tsconfig.app.json` file when creating an AdonisJS application. (Comes pre-configured with starter kits). 29 | 30 | ```jsonc 31 | { 32 | "extends": "@adonisjs/tsconfig/tsconfig.app.json", 33 | "compilerOptions": { 34 | "rootDir": "./", 35 | "outDir": "./build" 36 | } 37 | } 38 | ``` 39 | 40 | Extend from the `tsconfig.package.json` file when creating a package for the AdonisJS ecosystem. 41 | 42 | ```jsonc 43 | { 44 | "extends": "@adonisjs/tsconfig/tsconfig.package.json", 45 | "compilerOptions": { 46 | "rootDir": "./", 47 | "outDir": "./build" 48 | } 49 | } 50 | ``` 51 | 52 | ## Prettier config 53 | The [`@adonisjs/prettier-config`](https://github.com/adonisjs/tooling-config/tree/main/packages/prettier-config) package contains the base configuration to auto-format the source code for consistent styling. Feel free to explore configuration options inside the [index.json file](https://github.com/adonisjs/tooling-config/blob/main/packages/prettier-config/index.json). 54 | 55 | You can install the package and use it as follows. 56 | 57 | ```sh 58 | npm i -D @adonisjs/prettier-config 59 | 60 | # Make sure also to install prettier 61 | npm i -D prettier 62 | ``` 63 | 64 | Define the following property inside the `package.json` file. 65 | 66 | ```jsonc 67 | { 68 | "prettier": "@adonisjs/prettier-config" 69 | } 70 | ``` 71 | 72 | Also, create a `.prettierignore` file to ignore specific files and directories. 73 | 74 | ``` 75 | // title: .prettierignore 76 | build 77 | node_modules 78 | ``` 79 | 80 | ## ESLint config 81 | The [`@adonisjs/eslint-config`](https://github.com/adonisjs/tooling-config/tree/main/packages/eslint-config) package contains the base configuration to apply the linting rules. Feel free to explore options inside the [base config file](https://github.com/adonisjs/tooling-config/blob/main/packages/eslint-config/presets/ts_base.js), [application config file](https://github.com/adonisjs/tooling-config/blob/main/packages/eslint-config/presets/ts_app.js), and [package development config file](https://github.com/adonisjs/tooling-config/blob/main/packages/eslint-config/presets/ts_package.js). 82 | 83 | You can install the package and use it as follows. 84 | 85 | :::note 86 | 87 | Our config preset uses the [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) to ensure ESLint and Prettier can work together without stepping over each other. 88 | 89 | ::: 90 | 91 | ```sh 92 | npm i -D @adonisjs/eslint-config 93 | 94 | # Make sure also to install eslint 95 | npm i -D eslint 96 | ``` 97 | 98 | Extend from the `eslint-config/app` file when creating an AdonisJS application. (Comes pre-configured with starter kits). 99 | 100 | ```json 101 | // title: package.json 102 | { 103 | "eslintConfig": { 104 | "extends": "@adonisjs/eslint-config/app" 105 | } 106 | } 107 | ``` 108 | 109 | Extend from the `eslint-config/package` file when creating a package for the AdonisJS ecosystem. 110 | 111 | ```json 112 | // title: package.json 113 | { 114 | "eslintConfig": { 115 | "extends": "@adonisjs/eslint-config/package" 116 | } 117 | } 118 | ``` 119 | -------------------------------------------------------------------------------- /content/docs/database/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Available options for SQL libraries and ORMs in AdonisJS applications. 3 | --- 4 | 5 | # SQL and ORMs 6 | 7 | SQL databases are popular for storing the application's data in persistent storage. You can use any libraries and ORMs to make SQL queries inside an AdonisJS application. 8 | 9 | :::note 10 | The AdonisJS core team built the [Lucid ORM](./lucid.md) but does not force you to use it. You can use any other SQL libraries and ORMs you would like inside an AdonisJS application. 11 | ::: 12 | 13 | ## Popular options 14 | 15 | Following is the list of other popular SQL libraries and ORMs you can use inside an AdonisJS application (just like any other Node.js application). 16 | 17 | - [**Lucid**](./lucid.md) is a SQL query builder and an **Active Record ORM** built on top of [Knex](https://knexjs.org) created and maintained by the AdonisJS core team. 18 | - [**Prisma**](https://prisma.io/orm) Prisma ORM is another popular ORM in the Node.js ecosystem. It has a large community following. It offers intuitive data models, automated migrations, type-safety & auto-completion. 19 | - [**Kysely**](https://kysely.dev/docs/getting-started) is an end-to-end type safe query builder for Node.js. Kysely is a great fit if you need a lean query builder without any models. We have written an article explaining [how you can integrate Kysely inside an AdonisJS application](https://adonisjs.com/blog/kysely-with-adonisjs). 20 | - [**Drizzle ORM**](https://orm.drizzle.team/) is used by many AdonisJS developers in our community. We do not have any experience using this ORM, but you might want to check it out and see if it's an excellent fit for your use case. 21 | - [**Mikro ORM**](https://mikro-orm.io/docs/guide/first-entity) is an underrated ORM in the Node.js ecosystem. MikroORM is a little verbose in comparison to Lucid. However, it is actively maintained and also built on top of Knex. 22 | - [**TypeORM**](https://typeorm.io) is a popular ORM in the TypeScript ecosystem. 23 | 24 | ## Using other SQL libraries and ORMs 25 | 26 | When using another SQL library or ORM, you will have to change the configuration of some packages manually. 27 | 28 | ### Authentication 29 | 30 | The [AdonisJS authentication module](../authentication/introduction.md) comes with built-in support for Lucid to fetch the authenticated user. When using another SQL library or ORM, you will have to implement the `SessionUserProviderContract` or the `AccessTokensProviderContract` interface to fetch the user. 31 | 32 | Here is an example of how you can implement the `SessionUserProviderContract` interface when using `Kysely`. 33 | 34 | ```ts 35 | import { symbols } from '@adonisjs/auth' 36 | import type { SessionGuardUser, SessionUserProviderContract } from '@adonisjs/auth/types/session' 37 | import type { Users } from '../../types/db.js' // Specific to Kysely 38 | 39 | export class SessionKyselyUserProvider implements SessionUserProviderContract { 40 | /** 41 | * Used by the event emitter to add type information to the events emitted by the session guard. 42 | */ 43 | declare [symbols.PROVIDER_REAL_USER]: Users 44 | 45 | /** 46 | * Bridge between the session guard and your provider. 47 | */ 48 | async createUserForGuard(user: Users): Promise> { 49 | return { 50 | getId() { 51 | return user.id 52 | }, 53 | getOriginal() { 54 | return user 55 | }, 56 | } 57 | } 58 | 59 | /** 60 | * Find a user using the user id using your custom SQL library or ORM. 61 | */ 62 | async findById(identifier: number): Promise | null> { 63 | const user = await db 64 | .selectFrom('users') 65 | .selectAll() 66 | .where('id', '=', identifier) 67 | .executeTakeFirst() 68 | 69 | if (!user) { 70 | return null 71 | } 72 | 73 | return this.createUserForGuard(user) 74 | } 75 | } 76 | ``` 77 | 78 | Once you have implemented the `UserProvider` interface, you can use it inside your configuration. 79 | 80 | ```ts 81 | const authConfig = defineConfig({ 82 | default: 'web', 83 | 84 | guards: { 85 | web: sessionGuard({ 86 | useRememberMeTokens: false, 87 | provider: sessionUserProvider({ 88 | model: () => import('#models/user'), 89 | }), 90 | 91 | provider: configProvider.create(async () => { 92 | const { SessionKyselyUserProvider } = await import( 93 | '../app/auth/session_user_provider.js' // Path to the file 94 | ) 95 | 96 | return new SessionKyselyUserProvider() 97 | }), 98 | }), 99 | }, 100 | }) 101 | ``` 102 | -------------------------------------------------------------------------------- /content/docs/digging_deeper/ally_autocomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/digging_deeper/ally_autocomplete.png -------------------------------------------------------------------------------- /content/docs/digging_deeper/repl.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: AdonisJS offers an application-aware REPL to interact with your application from the command line. 3 | --- 4 | 5 | # REPL 6 | Like the [Node.js REPL](https://nodejs.org/api/repl.html), AdonisJS offers an application-aware REPL to interact with your application from the command line. You can start the REPL session using the `node ace repl` command. 7 | 8 | ```sh 9 | node ace repl 10 | ``` 11 | 12 | ![](../ace/ace_repl.png) 13 | 14 | On top of a standard Node.js REPL, AdonisJS provides the following features. 15 | 16 | - Import and execute TypeScript files. 17 | - Shorthand methods to import container services like the `router`, `helpers`, `hash` service, and so on. 18 | - Shorthand method to make class instances using the [IoC container](../concepts/dependency_injection.md#constructing-a-tree-of-dependencies). 19 | - Extensible API to add custom methods and REPL commands. 20 | 21 | ## Interacting with REPL 22 | Once you start the REPL session, you will see an interactive prompt in which you can write valid JavaScript code and press enter to execute it. The output of the code will be printed on the following line. 23 | 24 | If you want to type multiple lines of code, you can enter into the editor mode by typing the `.editor` command. Press `Ctrl+D` to execute a multiline statement or `Ctrl+C` to cancel and exit the editor mode. 25 | 26 | ```sh 27 | > (js) .editor 28 | # // Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel) 29 | ``` 30 | 31 | ### Accessing the result of the last executed command 32 | If you forget to assign the value of a statement to a variable, you can access it using the `_` variable. For example: 33 | 34 | ```sh 35 | > (js) helpers.string.generateRandom(32) 36 | # 'Z3y8QQ4HFpYSc39O2UiazwPeKYdydZ6M' 37 | > (js) _ 38 | # 'Z3y8QQ4HFpYSc39O2UiazwPeKYdydZ6M' 39 | > (js) _.length 40 | # 32 41 | > (js) 42 | ``` 43 | 44 | ### Accessing error raised by last executed command 45 | You can access the exception raised by the previous command using the `_error` variable. For example: 46 | 47 | ```sh 48 | > (js) helpers.string.generateRandom() 49 | > (js) _error.message 50 | # 'The value of "size" is out of range. It must be >= 0 && <= 2147483647. Received NaN' 51 | ``` 52 | 53 | ### Searching through history 54 | The REPL history is saved in the `.adonisjs_v6_repl_history` file in the user's home directory. 55 | 56 | You can loop through the commands from the history by pressing the up arrow `↑` key or pressing `Ctrl+R` to search within the history. 57 | 58 | ### Exiting from REPL session 59 | You can exit the REPL session by typing `.exit` or press the `Ctrl+C` twice. AdonisJS will perform a graceful shutdown before closing the REPL session. 60 | 61 | Also, if you modify your codebase, you must exit and restart the REPL session for new changes to pick up. 62 | 63 | ## Importing modules 64 | Node.js does not allow using the `import` statements inside the REPL session. Therefore, you must use the dynamic `import` function and assign the output to a variable. For example: 65 | 66 | ```ts 67 | const { default: User } = await import('#models/user') 68 | ``` 69 | 70 | You can use the `importDefault` method to access default export without destructuring the exports. 71 | 72 | ```ts 73 | const User = await importDefault('#models/user') 74 | ``` 75 | 76 | ## Helpers methods 77 | Helper methods are shortcut functions you can execute to perform specific actions. You can view the list of available methods using the `.ls` command. 78 | 79 | ```sh 80 | > (js) .ls 81 | 82 | # GLOBAL METHODS: 83 | importDefault Returns the default export for a module 84 | make Make class instance using "container.make" method 85 | loadApp Load "app" service in the REPL context 86 | loadEncryption Load "encryption" service in the REPL context 87 | loadHash Load "hash" service in the REPL context 88 | loadRouter Load "router" service in the REPL context 89 | loadConfig Load "config" service in the REPL context 90 | loadTestUtils Load "testUtils" service in the REPL context 91 | loadHelpers Load "helpers" module in the REPL context 92 | clear Clear a property from the REPL context 93 | p Promisify a function. Similar to Node.js "util.promisify" 94 | ``` 95 | 96 | ## Adding custom methods to REPL 97 | You can add custom methods to the REPL using `repl.addMethod`. The method accepts the name as the first argument and the implementation callback as the second argument. 98 | 99 | For demonstration, let's create a [preload file](../concepts/adonisrc_file.md#preloads) file and define a method to import all models from the `./app/models` directory. 100 | 101 | ```sh 102 | node ace make:preload repl -e=repl 103 | ``` 104 | 105 | ```ts 106 | // title: start/repl.ts 107 | import app from '@adonisjs/core/services/app' 108 | import repl from '@adonisjs/core/services/repl' 109 | import { fsImportAll } from '@adonisjs/core/helpers' 110 | 111 | repl.addMethod('loadModels', async () => { 112 | const models = await fsImportAll(app.makePath('app/models')) 113 | repl.server!.context.models = models 114 | 115 | repl.notify('Imported models. You can access them using the "models" property') 116 | repl.server!.displayPrompt() 117 | }) 118 | ``` 119 | 120 | You can pass the following options to the `repl.addMethod` method as the third argument. 121 | 122 | - `description`: Human-readable description to display in the help output. 123 | - `usage`: Define the method usage code snippet. If not set, the method name will be used. 124 | 125 | Once done, you can restart the REPL session and execute the `loadModels` method to import all the models. 126 | 127 | ```sh 128 | node ace repl 129 | 130 | # Type ".ls" to a view list of available context methods/properties 131 | > (js) await loadModels() 132 | ``` 133 | -------------------------------------------------------------------------------- /content/docs/getting_started/configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn how to read and update configuration values in AdonisJS. 3 | --- 4 | 5 | # Configuration 6 | 7 | The configuration files of your AdonisJS application are stored inside the `config` directory. A brand new AdonisJS application comes with a handful of pre-existing files used by the framework core and installed packages. 8 | 9 | Feel free to create additional files your application requires inside the `config` directory. 10 | 11 | :::note 12 | 13 | We recommend using [environment variables](./environment_variables.md) for storing secrets and environment-specific configuration. 14 | 15 | ::: 16 | 17 | ## Importing config files 18 | 19 | You may import the configuration files within your application codebase using the standard JavaScript `import` statement. For example: 20 | 21 | ```ts 22 | import { appKey } from '#config/app' 23 | ``` 24 | 25 | ```ts 26 | import databaseConfig from '#config/database' 27 | ``` 28 | 29 | When you import a config files, you get access to the exported values. In most cases, these exports are [`ConfigProvider`](../concepts/config_providers.md) instances, so directly using their values is not recommended. Instead, read the values from [the resolved config](../concepts/config_providers.md#how-do-i-access-the-resolved-config). 30 | 31 | ## Using the config service 32 | 33 | The config service offers an alternate API for reading the configuration values. In the following example, we use the config service to read the `appKey` value stored within the `config/app.ts` file. 34 | 35 | ```ts 36 | import config from '@adonisjs/core/services/config' 37 | 38 | config.get('app.appKey') 39 | config.get('app.http.cookie') // read nested values 40 | ``` 41 | 42 | The `config.get` method accepts a dot-separated key and parses it as follows. 43 | 44 | - The first part is the filename from which you want to read the values. I.e., `app.ts` file. 45 | - The rest of the string fragment is the key you want to access from the exported values. I.e., `appKey` in this case. 46 | 47 | ### Config service vs. directly importing config files 48 | 49 | Using the config service over directly importing the config files has no direct benefits. However, the config service is the only choice to read the configuration in external packages and edge templates. 50 | 51 | ### Reading config inside external packages 52 | 53 | If you are creating a third-party package, you should not directly import the config files from the user application because it will make your package tightly coupled with the folder structure of the host application. 54 | 55 | Instead, you should use the config service to access the config values inside a service provider. For example: 56 | 57 | ```ts 58 | import { ApplicationService } from '@adonisjs/core/types' 59 | 60 | export default class DriveServiceProvider { 61 | constructor(protected app: ApplicationService) {} 62 | 63 | register() { 64 | this.app.container.singleton('drive', () => { 65 | // highlight-start 66 | const driveConfig = this.app.config.get('drive') 67 | return new DriveManager(driveConfig) 68 | // highlight-end 69 | }) 70 | } 71 | } 72 | ``` 73 | 74 | ### Reading config inside Edge templates 75 | 76 | You may access configuration values inside edge templates using the `config` global method. 77 | 78 | ```edge 79 | Home 80 | ``` 81 | 82 | You can use the `config.has` method to check if a configuration value exists for a given key. The method returns `false` if the value is `undefined`. 83 | 84 | ```edge 85 | @if(config.has('app.appUrl')) 86 | Home 87 | @else 88 | Home 89 | @end 90 | ``` 91 | 92 | ## Changing the config location 93 | 94 | You can update the location for the config directory by modifying the [`adonisrc.ts`](../concepts/adonisrc_file.md) file. After the change, the config files will be imported from the new location. 95 | 96 | ```ts 97 | directories: { 98 | config: './configurations' 99 | } 100 | ``` 101 | 102 | Make sure to update the import alias within the `package.json` file. 103 | 104 | ```json 105 | { 106 | "imports": { 107 | "#config/*": "./configurations/*.js" 108 | } 109 | } 110 | ``` 111 | 112 | ## Config files limitations 113 | 114 | The config files stored within the `config` directory are imported during the boot phase of the application. As a result, the config files cannot rely on the application code. 115 | 116 | For example, if you try to import and use the router service inside the `config/app.ts` file, the application will fail to start. This is because the router service is not configured until the app is in a `booted` state. 117 | 118 | Fundamentally, this limitation positively impacts your codebase because the application code should rely on the config, not vice versa. 119 | 120 | ## Updating config at runtime 121 | 122 | You can mutate the config values at runtime using the config service. The `config.set` updates the value within the memory, and no changes are made to the files on the disk. 123 | 124 | :::note 125 | 126 | The config value is mutated for the entire application, not just for a single HTTP request. This is because Node.js is not a threaded runtime, and the memory in Node.js is shared between multiple HTTP requests. 127 | 128 | ::: 129 | 130 | ```ts 131 | import env from '#start/env' 132 | import config from '@adonisjs/core/services/config' 133 | 134 | const HOST = env.get('HOST') 135 | const PORT = env.get('PORT') 136 | 137 | config.set('app.appUrl', `http://${HOST}:${PORT}`) 138 | ``` 139 | -------------------------------------------------------------------------------- /content/docs/getting_started/env_intellisense.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/getting_started/env_intellisense.jpeg -------------------------------------------------------------------------------- /content/docs/getting_started/env_intellisense_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/getting_started/env_intellisense_original.png -------------------------------------------------------------------------------- /content/docs/old/forms_and_validation/custom_messages.md: -------------------------------------------------------------------------------- 1 | # Custom messages -------------------------------------------------------------------------------- /content/docs/old/forms_and_validation/pika-1675149043774-1x.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/old/forms_and_validation/pika-1675149043774-1x.jpeg -------------------------------------------------------------------------------- /content/docs/old/forms_and_validation/validating_api_requests.md: -------------------------------------------------------------------------------- 1 | # Validating API requests 2 | 3 | In AdonisJS, the API requests are validated at the controller layer. If the request validation fails, you send a `422 (Unprocessable Entity )` response with the error messages to the client. Otherwise continue with the rest of the controller logic. 4 | 5 | The API requests validation logic in AdonisJS is framework agnostic and you can use any validation library you like. However, we ship a great validator as part of the framework core. AdonisJS validator is: 6 | 7 | - One of the [fastest validator in the Node.js ecosystem](https://github.com/adonisjs/validator/blob/main/benchmarks.md). 8 | - Provides static and runtime safety. 9 | - Allows defining custom error messages. 10 | - Allows defining translations for error messages and fields. 11 | - Uses error formatters to format the shape of errors JSON. 12 | 13 | ## Basic example 14 | 15 | In this example, we will use 16 | -------------------------------------------------------------------------------- /content/docs/old/forms_and_validation/validation.md: -------------------------------------------------------------------------------- 1 | # Validation 2 | 3 | {{TOC}} 4 | 5 | In AdonisJS, the request data is validated at the controller layer. If the validation fails, you return the error messages to the client. Otherwise, continue with the rest of the controller logic. 6 | 7 | The data validation layer in AdonisJS is framework agnostic, and you can use any validation library you like. However, we ship a great validator as part of the framework core. AdonisJS validator is: 8 | 9 | - One of the [fastest validators in the Node.js ecosystem](https://github.com/adonisjs/validator/blob/main/benchmarks.md). 10 | - Provides static and runtime safety. 11 | - Casts form input values to JavaScript data types. 12 | - Allows defining custom error messages. 13 | - Allows defining translations for error messages and fields. 14 | 15 | ## Basic example 16 | 17 | Following is a basic example of creating a validation schema and using the `request.validate` method to perform the validation. Make sure to read our dedicated guides on [validating server-rendered forms](validating_server_rendered_forms.md) and [validating API requests](validating_api_requests.md). 18 | 19 | ```ts 20 | import router from '@adonisjs/core/services/router' 21 | import { schema, rules } from '@adonisjs/core/validator' 22 | 23 | router.post('register', async ({ request }) => { 24 | const registerUserSchema = schema.create({ 25 | email: schema.string([ 26 | rules.email(), 27 | rules.unique({ table: 'users' }) 28 | ]), 29 | 30 | password: schema.string([ 31 | rules.minLength(6), 32 | rules.maxLength(40) 33 | ]) 34 | }) 35 | 36 | await request.validate({ 37 | schema: registerUserSchema 38 | }) 39 | }) 40 | ``` 41 | 42 | The `request.validate` method validates the request body against the given schema. If the validation fails, the [`ValidationException` exception](#the-validationexception) is thrown. 43 | 44 | ## Static type-safety 45 | 46 | The AdonisJS validator infers the static types of the validated data from the schema. As a result, you get both static and runtime safety from a single schema definition. 47 | 48 | validator-static-types.webp 49 | 50 | ## Validator standalone usage 51 | 52 | You can use the validator outside an HTTP request by importing the `validate` method from the validator module. When using the standalone `validate` method, you must manually provide the `data` object. 53 | 54 | ```ts 55 | import { schema, rules, validate } from '@adonisjs/core/validator' 56 | 57 | const registerUserSchema = schema.create({ 58 | // ... fields and rules 59 | }) 60 | 61 | try { 62 | await validate({ 63 | schema, 64 | data: { 65 | email: 'virk@adonisjs.com', 66 | password: 'secret' 67 | } 68 | }) 69 | } catch (error) { 70 | console.log(error.messages) 71 | } 72 | ``` 73 | 74 | ## The `ValidationException` 75 | 76 | The `ValidationException` is thrown by the `request.validate` and the `validate` methods when the data validation fails. You can access the validation errors using the `error.messages` property. The structure of error messages depends upon the configured [error formatter](./error_formatters.md). 77 | 78 | During an HTTP request, we recommend you not to handle the `ValidationException` and instead let it reach the [global exception handler](../basics/exceptions_handling.md). 79 | 80 | The global exception handler will convert the `ValidationException` to an HTTP response using [content negotiation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation). Following is a brief explanation of how content negotiation works. 81 | 82 | - If the request has an `Accept` header set to `application/json`, we will send the validation error messages as an array of JSON objects. 83 | - If the request has an `Accept` header set to `application/vnd.api+json `, then we will send the validation errors in [JSON API](https://jsonapi.org/format/#errors) format. 84 | - Otherwise, we respond with a redirect and share the validation errors using session flash messages. 85 | 86 | Whether you are building an API server or a server-rendered application, the content negotiation will ensure that the client receives the error messages in the best format possible. 87 | 88 | ## Casting input fields to JavaScript data types 89 | 90 | Data sent over HTTP is always represented as a string. So, for example, if you create an input field of type `number` and send its value to the backend, you will receive a **string representation of number** and not the **number data-type**. 91 | 92 | At some stage, your backend server must cast string values to JavaScript data types. AdonisJS performs data casting at the time of validation. 93 | 94 | The schema methods like `schema.number`, or `schema.boolean` perform validations and type casting simultaneously. You can also add [extend the schema API]() with custom data types. 95 | 96 | ## Next steps 97 | 98 | - Read guides on [validating server-rendered forms](validating_server_rendered_forms.md) and [validating API requests](validating_api_requests.md). 99 | - Learn everything about the [creating validation schemas](schema_101.md). 100 | -------------------------------------------------------------------------------- /content/docs/old/forms_and_validation/validator-static-types.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/old/forms_and_validation/validator-static-types.webp -------------------------------------------------------------------------------- /content/docs/old/pages/adonisjs-at-a-glance.md: -------------------------------------------------------------------------------- 1 | # AdonisJS at a glance 2 | 3 | AdonisJS was first released in 2015 with the goal of creating a batteries included backend framework for Node.js. Since its inception, we have maintaining and improving the framework every single day and committed to do it in the future as well. 4 | 5 | ### Where does AdonisJS stands in the JavaScript frameworks landscape? 6 | 7 | The framework ecosystem in JavaScript is 8 | 9 | -------------------------------------------------------------------------------- /content/docs/old/resources/updates.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | Keep your AdonisJS applications up to date by staying on top of latest releases. 3 | 4 | ::component{template="partials/updates"} 5 | -------------------------------------------------------------------------------- /content/docs/old/tutorial/creating-new-application.md: -------------------------------------------------------------------------------- 1 | # Creating a new application 2 | 3 | Let's start by creating a new application on top of **web starter kit**. Starter kits in AdonisJS are pre-configured project structures to create a certain style of application. 4 | 5 | You can always refer the documentation to [learn more about starter kits](../docs/installation.md#starter-kits). But, for this tutorial, make sure to select the web starter kit as it includes everything we need to create build the Hacker News clone. 6 | 7 | ```sh 8 | npm init adonisjs@latest 9 | ``` 10 | 11 | Once done, you can start the development server using the `serve` command and view the welcome page in your browser by visiting the [http://localhost:3333](http://localhost:3333) URL. 12 | 13 | ```sh 14 | node ace serve --watch 15 | ``` 16 | 17 | VIDEO COMES HERE 18 | 19 | ## Exploring configured packages 20 | 21 | Let's kill the development server by pressing `Ctrl+C` on your keyboard and explore the packages installed and configured by the web starter kit. 22 | 23 | ```json 24 | { 25 | "" 26 | } 27 | ``` -------------------------------------------------------------------------------- /content/docs/old/tutorial/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Welcome to the AdonisJS tutorial for new comers. The purpose of this tutorial is to teach you how to create end to end web applications from scratch using AdonisJS. 4 | 5 | Throughout this tutorial, we will choose simple tools and techniques to build a hacker news clone. Simple is not bad, in fact it should be the norm in the world full of complex acronyms and libraries releasing every week. 6 | 7 | ## The tech stack 8 | 9 | - [**Edge.js**]() is a server rendered template engine for Node.js. It requires zero build tools and is a great fit for creating web applications with limited frontend interactivity. 10 | - [**Tailwind CSS**](https://tailwindcss.com) is a utility-first CSS framework. We will be using it to style our web application. 11 | - [**Unpoly**](https://unpoly.com) progressively enhances existing web applications without writing any custom frontend JavaScript. We will be using it to have SPA style navigation and perform partial page updates. 12 | - [**Vite**]() is a build system for frontend applications. We will be using it to compile TailwindCSS. Even though, we can compile TailwindCSS using its CLI, We think it will be worth learning how to setup Vite inside an AdonisJS application for your future projects. 13 | 14 | ## Pre-requisites 15 | 16 | We expect the readers of this tutorial to have basic understanding of JavaScript/TypeScript, HTML, CSS and might have some experience in creating Node.js applications. 17 | 18 | Since, this tutorial focuses on learning the framework (AdonisJS), we will not be discussing the basics of JavaScript or Node.js here. If you are new to programming and want to learn Node.js, then please visit [nodejs.dev](https://nodejs.dev/en/learn/). 19 | 20 | Before getting started, make sure you have Node.js and npm installed on your computer. AdonisJS requires `Node.js >= 18`. You can install Node.js from the [official website](https://nodejs.org/en/download/), it should only take a few minutes. 21 | 22 | ```sh 23 | # Verify Node.js version 24 | node -v 25 | # v18.11.0 26 | 27 | # Verify npm version 28 | npm -v 29 | # 8.19.2 30 | ``` 31 | 32 | ## Editor setup 33 | 34 | You can use any code editor of your choice as long as it has support for TypeScript. If you were to ask us, we will recommend using [VSCode](https://code.visualstudio.com) with the following extensions for a great development experience. 35 | 36 | - [AdonisJS VSCode extension](https://marketplace.visualstudio.com/items?itemName=jripouteau.adonis-vscode-extension) 37 | - [Japa VSCode extension](https://marketplace.visualstudio.com/items?itemName=jripouteau.japa-vscode) 38 | - [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) 39 | - [Prettier code formatter extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) -------------------------------------------------------------------------------- /content/docs/old/validator/custom_messages.md: -------------------------------------------------------------------------------- 1 | # Custom messages 2 | 3 | The `validate` method accepts the custom messages alongside the validation schema object. You can define messages just for the validation rules, or you can specify them for individual fields as well. 4 | 5 | ```ts 6 | await request.validate({ 7 | schema: schema.create({ 8 | // ... 9 | }), 10 | // highlight-start 11 | messages: { 12 | required: 'The {{ field }} is required to create a new account', 13 | 'username.unique': 'Username not available' 14 | } 15 | // highlight-end 16 | }) 17 | ``` 18 | 19 | - The custom message for the `required` rule will be used by all the fields that fail the required validation. 20 | - The `username.unique` combination applies only to the `username` field for the `unique` validation rule. 21 | 22 | Messages for nested objects and arrays can be defined using the dot separator. 23 | 24 | ```ts 25 | { 26 | messages: { 27 | 'user.username.required': 'Missing value for username', 28 | 'tags.*.number': 'Tags must be an array of numbers', 29 | 'products.*.title.required': 'Each product must have a title' 30 | } 31 | } 32 | ``` 33 | 34 | ## Dynamic placeholders 35 | You can make use of the following placeholders to reference runtime values inside your custom messages. 36 | 37 | ```ts 38 | { 39 | messages: { 40 | required: '{{ field }} is required to sign up', 41 | enum: 'The value of {{ field }} must be in {{ options.choices }}' 42 | } 43 | } 44 | ``` 45 | 46 | | Placeholder | Description | 47 | |-------------|-------------| 48 | | `{{ field }}` | Name of the field under validation. Nested object paths are represented with a dot separator. For example: `user.profile.username` | 49 | | `{{ rule }}` | Name of the validation rule | 50 | | `{{ options }}` | The options passed by the validation methods. For example, The `enum` rule will pass an array of `choices`, and some rules may not pass any options at all | 51 | 52 | ## Wildcard callback 53 | You can also define a callback function to construct the message at runtime. The callback can only be defined as a fallback using the wildcard `*` expression. 54 | 55 | The callback will be invoked for all the fields in the following example, except for the `username` field only when it fails the `required` validation. 56 | 57 | ```ts 58 | { 59 | messages: { 60 | '*': (field, rule, arrayExpressionPointer, options) => { 61 | return `${rule} validation error on ${field}` 62 | }, 63 | 'username.required': 'Username is required to sign up', 64 | } 65 | } 66 | ``` 67 | 68 | ## Options passed to the message string 69 | Following is the list of options passed by the different validation methods to the message string. 70 | 71 | ### date 72 | The `date` validation rule will pass the `options.format`. 73 | 74 | ```ts 75 | { 76 | 'date.format': '{{ field }} must be formatted as {{ options.format }}', 77 | } 78 | ``` 79 | 80 | --- 81 | 82 | ### distinct 83 | The `distinct` validation rule will pass the `field` on which the distinct rule is applied, along with the `index` at which the duplicate value was found. 84 | 85 | ```ts 86 | { 87 | 'products.distinct': 'The product at {{ options.index + 1 }} position has already been added earlier' 88 | } 89 | ``` 90 | 91 | --- 92 | 93 | ### enum / enumSet 94 | The `enum` and `enumSet` validation rules will pass an array of `options.choices`. 95 | 96 | ```ts 97 | { 98 | 'enum': 'The value must be one of {{ options.choices }}', 99 | 'enumSet': 'The values must be one of {{ options.choices }}', 100 | } 101 | ``` 102 | 103 | --- 104 | 105 | ### file 106 | The file validation allows defining custom messages for the sub-rules. For example: 107 | 108 | ```ts 109 | { 110 | 'file.size': 'The file size must be under {{ options.size }}', 111 | 'file.extname': 'The file must have one of {{ options.extnames }} extension names', 112 | } 113 | ``` 114 | 115 | --- 116 | 117 | ### minLength / maxLength 118 | The `minLength` and `maxLength` validation rules will pass the following options to custom messages. 119 | 120 | ```ts 121 | { 122 | 'minLength': 'The array must have minimum of {{ options.minLength }} items', 123 | 'maxLength': 'The array can contain maximum of {{ options.maxLength }} items', 124 | } 125 | ``` 126 | 127 | --- 128 | 129 | ### range 130 | The `range` validation rule passes the `start` and the `stop` options to custom messages. 131 | 132 | ```ts 133 | { 134 | 'range': 'Candidate age must be between {{ options.start }} and {{ options.stop }} years', 135 | } 136 | ``` 137 | 138 | --- 139 | 140 | ### requiredIfExists / requiredIfNotExists 141 | The `requiredIfExists` and `requiredIfNotExists` validation rules will pass the `options.otherField` as a string. 142 | 143 | ```ts 144 | { 145 | 'requiredIfExists': '{{ options.otherField }} requires {{ field }}', 146 | } 147 | ``` 148 | 149 | --- 150 | 151 | ### Conditional required rules 152 | The following `requiredIf*` rules will pass the `options.otherFields` as an array of strings. 153 | 154 | - requiredIfExistsAll 155 | - requiredIfExistsAny 156 | - requiredIfNotExistsAll 157 | - requiredIfNotExistsAny 158 | 159 | ```ts 160 | { 161 | 'requiredIfExistsAll': '{{ options.otherFields }} requires {{ field }}', 162 | } 163 | ``` 164 | 165 | --- 166 | 167 | ### requiredWhen 168 | The `requiredWhen` validation rule will pass the following options. 169 | 170 | - `options.otherField` 171 | - `options.operator` 172 | - `options.values` 173 | 174 | 175 | ```ts 176 | { 177 | 'requiredWhen': '{{ field }} is required when {{ otherField }}{{ operator }}{{ values }}' 178 | } 179 | ``` 180 | -------------------------------------------------------------------------------- /content/docs/old/validator/schema_caching.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Schema caching 4 | 5 | The schema created using the `schema.create` method is first complied to an executable function and then executed to validate the data against the defined rules. 6 | 7 | The compilation process does take a couple of milliseconds before the validation begins. However, based on your performance expectations, you may want to consider caching the compiled schema and hence don't pay the compilation penalty on every request. 8 | 9 | ## Using the `cacheKey` 10 | You can cache a schema by defining a unique `cacheKey`. You can generate this cache key using any approach or rely on the `ctx.routeKey` during an HTTP request. 11 | 12 | ```ts 13 | await request.validate({ 14 | schema: schema.create({...}), 15 | cacheKey: ctx.routeKey, 16 | }) 17 | ``` 18 | 19 | - The first call to `request.validate` will compile the schema and saves the output in reference to the `cacheKey`. 20 | - Until the `cacheKey` is identical, the validator won't recompile the schema. 21 | 22 | ## Caching caveats 23 | Caching in any form is not free, and the same is the case with schema caching. If your schema relies on runtime values, then caching schema will not give the desired outcome. Consider the following example: 24 | 25 | - You are creating a form that accepts the user **state** and their **city**. 26 | - The city options are based upon the value of the selected **state**. 27 | 28 | ```ts 29 | /** 30 | * Assuming the following variables hold data 31 | */ 32 | const STATES = [] 33 | const CITIES = {} 34 | 35 | export default class AddressValidator { 36 | public selectedState = this.ctx.request.input('state') // 👈 37 | 38 | public schema = schema.create({ 39 | state: schema.enum(STATES), 40 | city: schema.enum(CITIES[this.selectedState] || []) 41 | }) 42 | } 43 | ``` 44 | 45 | If you look at the above example, the enum options for the `city` are dependent on the `selectedState` and may vary with every HTTP request. 46 | 47 | However, since we have schema caching turned on. The enum options after the first request will get cached and will not change even if the user selects a different state. 48 | 49 | Now that you understand how caching works. Let's explore some different ways to use dynamic data within your validation schema. 50 | 51 | ### Give up caching 52 | The first option is to give up caching. This will add a delay of a couple of milliseconds to your requests but gives you the most straightforward API to use runtime values within your schema definition. 53 | 54 | ### Create a unique key 55 | Considering the above example, you can append the selected state to the `cacheKey`, and hence each state will have its copy of cached schema. For example: 56 | 57 | ```ts 58 | export default class AddressValidator { 59 | public selectedState = this.ctx.request.input('state') 60 | 61 | public schema = schema.create({ 62 | state: schema.enum(STATES), 63 | city: schema.enum(CITIES[this.selectedState] || []) 64 | }) 65 | 66 | // highlight-start 67 | public cacheKey = `${this.ctx.routeKey}-${selectedState}` 68 | // highlight-end 69 | } 70 | ``` 71 | 72 | The above approach has its own set of downsides. For example, if there are 37 states, there will be 37 cached copies of the same schema with a slight variation. Also, this number will grow exponentially if you need more than one dynamic value. 73 | 74 | Giving up caching is better than caching too many schemas with slight variations. 75 | 76 | ### Using refs 77 | Refs give you the best of both worlds. You can still cache your schema and also reference the runtime values inside them. Following is an example of the same: 78 | 79 | ```ts 80 | export default class AddressValidator { 81 | public selectedState = this.ctx.request.input('state') 82 | 83 | // highlight-start 84 | public refs = schema.refs({ 85 | cities: CITIES[this.selectedState] || [] 86 | }) 87 | // highlight-end 88 | 89 | public schema = schema.create({ 90 | state: schema.enum(STATES), 91 | // highlight-start 92 | city: schema.enum(this.refs.cities) 93 | // highlight-end 94 | }) 95 | } 96 | ``` 97 | 98 | Instead of referencing `CITIES[this.selectedState]` directly, you move it to the `schema.refs` object, and from there on, the cities will be picked up at runtime without recompiling the schema. 99 | 100 | :::note 101 | 102 | Refs only work if the **validation rule** or the **schema type** supports them. 103 | 104 | ::: 105 | -------------------------------------------------------------------------------- /content/docs/preface/faqs.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Frequently asked questions about AdonisJS, the framework, and its ecosystem. 3 | --- 4 | 5 | # FAQs 6 | 7 | ## Who maintains AdonisJS? 8 | 9 | AdonisJS is an independent project created by [Harminder Virk](https://twitter.com/AmanVirk1) in 2015. The framework is actively maintained by the [core team](https://github.com/orgs/adonisjs/people) and community contributors. 10 | 11 | The framework creator (Harminder Virk) is the project lead and works full-time on the framework. 12 | 13 | The project is funded through GitHub Sponsors. If you or your business benefit from AdonisJS, consider [sponsoring us to support the framework development](https://github.com/sponsors/thetutlage). 14 | 15 | ## How is AdonisJS licensed? 16 | 17 | AdonisJS (the framework) and the official packages are distributed under the [MIT License](https://opensource.org/license/mit/). In addition, the source code is publicly available on [GitHub](https://github.com/adonisjs). 18 | 19 | ## Is AdonisJS reliable and well-maintained? 20 | 21 | AdonisJS is used in production by [Marie Claire](https://www.marieclaire.com/), [Cleavr](https://cleavr.io), [Ledger](https://www.ledger.com/), [Cavai](https://cavai.com), [Kayako](https://kayako.com), [Renault Group](https://www.renaultgroup.com/en/), [Zakodium](https://www.zakodium.com/), [FIVB](https://www.fivb.com), and many more companies in varying capacities. 22 | 23 | The framework creator works full-time on AdonisJS and ensures the framework is actively improved and maintained. 24 | 25 | - During the v6 release, we migrated to the ES module system. 26 | - Officially maintained packages have zero security vulnerabilities reported by Snyk’s security scan. 27 | - We continuously work towards writing better documentation, releasing new features, and improving the existing codebase. 28 | 29 | ## Is AdonisJS fast? 30 | 31 | When creating the framework or adding new features, we primarily focus on solving real-world problems rather than cutting down the functionality to make AdonisJS win the benchmark Olympics. 32 | 33 | However, we look closer at the performance metrics and fine-tune the framework performance wherever it matters. For example: 34 | 35 | - The AdonisJS HTTP server in standalone mode is [on par with Fastify in performance](https://github.com/adonisjs/http-server/blob/main/benchmarks.md). 36 | - The validation layer of the framework [outperforms other popular validation libraries](https://github.com/vinejs/vine/blob/main/benchmarks.md) in the Node.js ecosystem. 37 | 38 | ## Do you offer paid support? 39 | Yes! On our website, you can learn more about the [priority support program](https://adonisjs.com/contact). 40 | 41 | ## How do I stay up to date with AdonisJS? 42 | Check out the following links to stay connected and up-to-date. 43 | 44 | - [Discord server](https://discord.gg/vDcEjq6) 45 | - [X (Formerly Twitter)](https://twitter.com/adonisframework) 46 | - [GitHub discussions](https://github.com/orgs/adonisjs/discussions) 47 | - [Blog and Newsletter](https://adonisjs.com/blog?referrer=adonisjs_docs_faq) 48 | - [Adocasts](https://adocasts.com/?referrer=adonisjs_docs_faq) 49 | -------------------------------------------------------------------------------- /content/docs/preface/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: "AdonisJS is a TypeScript-first web framework for Node.js. You can use it to create a full-stack web application or a JSON API server." 3 | --- 4 | 5 | # Introduction 6 | 7 | ::include{template="partials/introduction_cards"} 8 | 9 | ## What is AdonisJS? 10 | 11 | AdonisJS is a TypeScript-first web framework for Node.js. You can use it to create a full-stack web application or a JSON API server. 12 | 13 | At the fundamental level, AdonisJS [provides structure to your applications](../getting_started/folder_structure.md), configures a [seamless TypeScript development environment](../concepts/typescript_build_process.md), configures [HMR](../concepts/hmr.md) for your backend code, and offers a vast collection of well-maintained and extensively documented packages. 14 | 15 | We envision teams using AdonisJS **spending less time** on trivial decisions like cherry-picking npm packages for every minor feature, writing glue code, debating for the perfect folder structure, and **spending more time** delivering real-world features critical for the business needs. 16 | 17 | ### Frontend agnostic 18 | 19 | AdonisJS focuses on the backend and lets you choose the frontend stack of your choice. 20 | 21 | If you like to keep things simple, pair AdonisJS with a [traditional template engine](../views-and-templates/introduction.md) to generate static HTML on the server, create a JSON API for your frontend Vue/React application or use [Inertia](../views-and-templates/inertia.md) to make your favorite frontend framework work together in perfect harmony. 22 | 23 | AdonisJS aims to provide you with batteries to create a robust backend application from scratch. Be it sending emails, validating user input, performing CRUD operations, or authenticating users. We take care of it all. 24 | 25 | ### Modern and Type-safe 26 | 27 | AdonisJS is built on top of modern JavaScript primitives. We use ES modules, Node.js sub-path import aliases, SWC for executing TypeScript source, and Vite for assets bundling. 28 | 29 | 30 | Also, TypeScript plays a considerable role when designing the framework's APIs. For example, AdonisJS has: 31 | 32 | - [Type-safe event emitter](../digging_deeper/emitter.md#making-events-type-safe) 33 | - [Type-safe environment variables](../getting_started/environment_variables.md) 34 | - [Type-safe validation library](../basics/validation.md) 35 | 36 | ### Embracing MVC 37 | 38 | AdonisJS embraces the classic MVC design pattern. You start by defining the routes using the functional JavaScript API, bind controllers to them and write logic to handle the HTTP requests within the controllers. 39 | 40 | ```ts 41 | // title: start/routes.ts 42 | import router from '@adonisjs/core/services/router' 43 | const PostsController = () => import('#controllers/posts_controller') 44 | 45 | router.get('posts', [PostsController, 'index']) 46 | ``` 47 | 48 | Controllers can use models to fetch data from the database and render a view (aka template) as a response. 49 | 50 | ```ts 51 | // title: app/controllers/posts_controller.ts 52 | import Post from '#models/post' 53 | import type { HttpContext } from '@adonisjs/core/http' 54 | 55 | export default class PostsController { 56 | async index({ view }: HttpContext) { 57 | const posts = await Post.all() 58 | return view.render('pages/posts/list', { posts }) 59 | } 60 | } 61 | ``` 62 | 63 | If you are building an API server, you can replace the view layer with a JSON response. But, the flow of handling and responding to the HTTP requests remains the same. 64 | 65 | ```ts 66 | // title: app/controllers/posts_controller.ts 67 | import Post from '#models/post' 68 | import type { HttpContext } from '@adonisjs/core/http' 69 | 70 | export default class PostsController { 71 | async index({ view }: HttpContext) { 72 | const posts = await Post.all() 73 | // delete-start 74 | return view.render('pages/posts/list', { posts }) 75 | // delete-end 76 | // insert-start 77 | /** 78 | * Posts array will be serialized to JSON 79 | * automatically. 80 | */ 81 | return posts 82 | // insert-end 83 | } 84 | } 85 | ``` 86 | 87 | ## Guides assumptions 88 | 89 | The AdonisJS documentation is written as a reference guide, covering the usage and the API of several packages and modules maintained by the core team. 90 | 91 | **The guide does not teach you how to build an application from scratch**. If you are looking for a tutorial, we recommend starting your journey with [Adocasts](https://adocasts.com/). Tom (the creator of Adocasts) has created some high quality screencasts, helping you to take the first steps with AdonisJS. 92 | 93 | With that said, the documentation extensively covers the usage of available modules and the inner workings of the framework. 94 | 95 | ## Recent releases 96 | Following is the list of recent releases. [Click here](./releases.md) to view all the releases. 97 | 98 | ::include{template="partials/recent_releases"} 99 | 100 | ## Sponsors 101 | 102 | ::include{template="partials/sponsors"} 103 | -------------------------------------------------------------------------------- /content/docs/preface/releases.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Notable changes and new features in AdonisJS releases 3 | --- 4 | 5 | ::include{template="partials/releases"} 6 | -------------------------------------------------------------------------------- /content/docs/security/encryption.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Encrypt and decrypt values in your application using the encryption service. 3 | --- 4 | 5 | # Encryption 6 | 7 | Using the encryption service, you may encrypt and decrypt values in your application. The encryption is based on the [aes-256-cbc algorithm](https://www.n-able.com/blog/aes-256-encryption-algorithm), and we append an integrity hash (HMAC) to the final output to prevent value tampering. 8 | 9 | The `encryption` service uses the `appKey` stored inside the `config/app.ts` file as the secret to encrypt the values. 10 | 11 | - It is recommended to keep the `appKey` secure and inject it into your application using [environment variables](../getting_started/environment_variables.md). Anyone with access to this key can decrypt values. 12 | 13 | - The key should be at least 16 characters long and have a cryptographically secure random value. You may generate the key using the `node ace generate:key` command. 14 | 15 | - If you decide to change the key later, you will not be able to decrypt existing values. This will result in invalidating existing cookies and user sessions. 16 | 17 | ## Encrypting values 18 | 19 | You may encrypt values using the `encryption.encrypt` method. The method accepts the value to encrypt and an optional time duration after which to consider the value expired. 20 | 21 | ```ts 22 | import encryption from '@adonisjs/core/services/encryption' 23 | 24 | const encrypted = encryption.encrypt('hello world') 25 | ``` 26 | 27 | Define a time duration after which the value will be considered expired and cannot be decrypted. 28 | 29 | ```ts 30 | const encrypted = encryption.encrypt('hello world', '2 hours') 31 | ``` 32 | 33 | ## Decrypting values 34 | 35 | Encrypted values can be decrypted using the `encryption.decrypt` method. The method accepts the encrypted value as the first argument. 36 | 37 | ```ts 38 | import encryption from '@adonisjs/core/services/encryption' 39 | 40 | encryption.decrypt(encryptedValue) 41 | ``` 42 | 43 | ## Supported data types 44 | 45 | The value given to the `encrypt` method is serialized to a string using `JSON.stringify`. Therefore, you can use the following JavaScript data types. 46 | 47 | - string 48 | - number 49 | - bigInt 50 | - boolean 51 | - null 52 | - object 53 | - array 54 | 55 | ```ts 56 | import encryption from '@adonisjs/core/services/encryption' 57 | 58 | // Object 59 | encryption.encrypt({ 60 | id: 1, 61 | fullName: 'virk', 62 | }) 63 | 64 | // Array 65 | encryption.encrypt([1, 2, 3, 4]) 66 | 67 | // Boolean 68 | encryption.encrypt(true) 69 | 70 | // Number 71 | encryption.encrypt(10) 72 | 73 | // BigInt 74 | encryption.encrypt(BigInt(10)) 75 | 76 | // Data objects are converted to ISO string 77 | encryption.encrypt(new Date()) 78 | ``` 79 | 80 | ## Using custom secret keys 81 | 82 | You can create an [instance of the Encryption class](https://github.com/adonisjs/encryption/blob/main/src/encryption.ts) directly to use custom secret keys. 83 | 84 | ```ts 85 | import { Encryption } from '@adonisjs/core/encryption' 86 | 87 | const encryption = new Encryption({ 88 | secret: 'alongrandomsecretkey', 89 | }) 90 | ``` 91 | -------------------------------------------------------------------------------- /content/docs/testing/browser_tests_output.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/testing/browser_tests_output.jpeg -------------------------------------------------------------------------------- /content/docs/testing/browser_tests_output_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/content/docs/testing/browser_tests_output_original.png -------------------------------------------------------------------------------- /content/docs/testing/database.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: "Learn how to test code that interacts with your databases in AdonisJS: simple steps for setting up, resetting, and keeping databases clean during tests." 3 | --- 4 | 5 | # Database tests 6 | 7 | Database tests refer to testing how your application interacts with the database. This includes testing what is written to the database, how to run migrations before the tests, and how to keep the database clean between tests. 8 | 9 | ## Migrating the database 10 | 11 | Before executing your tests that interact with the database, you would want to run your migrations first. We have two hooks available in the `testUtils` service for that, which you can configure inside the `tests/bootstrap.ts` file. 12 | 13 | ### Reset database after each run cycle 14 | 15 | The first option we have is `testUtils.db().migrate()`. This hook will first run all your migrations, and then rollback everything. 16 | 17 | ```ts 18 | // title: tests/bootstrap.ts 19 | import testUtils from '@adonisjs/core/services/test_utils' 20 | 21 | export const runnerHooks: Required> = { 22 | setup: [ 23 | () => testUtils.db().migrate(), 24 | ], 25 | teardown: [], 26 | } 27 | ``` 28 | 29 | By configuring the hook here, what will happen is: 30 | 31 | - Before running our tests, the migrations will be executed. 32 | - At the end of our tests, the database will be rolled back. 33 | 34 | So, each time we run our tests, we will have a fresh and empty database. 35 | 36 | ### Truncate tables after each run cycle 37 | 38 | Resetting the database after each run cycle is a good option, but it can be slow if you have a lot of migrations. Another option is to truncate the tables after each run cycle. This option will be faster, as the migrations will only be executed once : the very first time you run your tests on a fresh database. 39 | 40 | 41 | At the end of each run cycle, the tables will just be truncated, but our schema will be kept. So, the next time we run our tests, we will have an empty database, but the schema will already be in place, so there's no need to run every migration again. 42 | 43 | ```ts 44 | // title: tests/bootstrap.ts 45 | import testUtils from '@adonisjs/core/services/test_utils' 46 | 47 | export const runnerHooks: Required> = { 48 | setup: [ 49 | () => testUtils.db().truncate(), 50 | ], 51 | } 52 | ``` 53 | 54 | ## Seeding the database 55 | 56 | If you need to seed your database, you can use the `testUtils.db().seed()` hook. This hook will run all your seeds before running your tests. 57 | 58 | ```ts 59 | // title: tests/bootstrap.ts 60 | import testUtils from '@adonisjs/core/services/test_utils' 61 | 62 | export const runnerHooks: Required> = { 63 | setup: [ 64 | () => testUtils.db().seed(), 65 | ], 66 | } 67 | ``` 68 | 69 | ## Keeping the database clean between tests 70 | 71 | ### Global transaction 72 | 73 | When running tests, you may want to keep your database clean between each test. For that, you can use the `testUtils.db().withGlobalTransaction()` hook. This hook will start a transaction before each test and roll it back at the end of the test. 74 | 75 | ```ts 76 | // title: tests/unit/user.spec.ts 77 | import { test } from '@japa/runner' 78 | import testUtils from '@adonisjs/core/services/test_utils' 79 | 80 | test.group('User', (group) => { 81 | group.each.setup(() => testUtils.db().withGlobalTransaction()) 82 | }) 83 | ``` 84 | 85 | Note that if you are using any transactions in your tested code, this will not work as transactions cannot be nested. In this case, you can use the `testUtils.db().migrate()` or `testUtils.db().truncate()` hook instead. 86 | 87 | ### Truncate tables 88 | 89 | As mentioned above, the global transaction will not work if you are using transactions in your tested code. In this case, you can use the `testUtils.db().truncate()` hook. This hook will truncate all your tables after each test. 90 | 91 | ```ts 92 | // title: tests/unit/user.spec.ts 93 | import { test } from '@japa/runner' 94 | 95 | test.group('User', (group) => { 96 | group.each.setup(() => testUtils.db().truncate()) 97 | }) 98 | ``` 99 | -------------------------------------------------------------------------------- /content/docs/testing/mocks_and_fakes.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn how to mock or fake dependencies during testing in AdonisJS. 3 | --- 4 | 5 | # Mocks and Fakes 6 | 7 | When testing your applications, you might want to mock or fake specific dependencies to prevent actual implementations from running. For example, you wish to refrain from emailing your customers when running tests and neither call third-party services like a payment gateway. 8 | 9 | AdonisJS offers a few different APIs and recommendations using which you can fake, mock, or stub a dependency. 10 | 11 | ## Using the fakes API 12 | 13 | Fakes are objects with working implementations explicitly created for testing. For example, The mailer module of AdonisJS has a fake implementation that you can use during testing to collect outgoing emails in memory and write assertions for them. 14 | 15 | We provide fake implementations for the following container services. The fakes API is documented alongside the module documentation. 16 | 17 | - [Emitter fake](../digging_deeper/emitter.md#faking-events-during-tests) 18 | - [Hash fake](../security/hashing.md#faking-hash-service-during-tests) 19 | - [Fake mailer](../digging_deeper/mail.md#fake-mailer) 20 | - [Drive fake](../digging_deeper/drive.md#faking-disks) 21 | 22 | ## Dependency injection and fakes 23 | 24 | If you use dependency injection in your application or use the [container to create class instances](../concepts/dependency_injection.md), you can provide a fake implementation for a class using the `container.swap` method. 25 | 26 | In the following example, we inject `UserService` to the `UsersController`. 27 | 28 | ```ts 29 | import UserService from '#services/user_service' 30 | import { inject } from '@adonisjs/core' 31 | 32 | export default class UsersController { 33 | @inject() 34 | index(service: UserService) {} 35 | } 36 | ``` 37 | 38 | During testing, we can provide an alternate/fake implementation of the `UserService` class as follows. 39 | 40 | ```ts 41 | import UserService from '#services/user_service' 42 | import app from '@adonisjs/core/services/app' 43 | 44 | test('get all users', async () => { 45 | // highlight-start 46 | class FakeService extends UserService { 47 | all() { 48 | return [{ id: 1, username: 'virk' }] 49 | } 50 | } 51 | 52 | /** 53 | * Swap `UserService` with an instance of 54 | * `FakeService` 55 | */ 56 | app.container.swap(UserService, () => { 57 | return new FakeService() 58 | }) 59 | 60 | /** 61 | * Test logic goes here 62 | */ 63 | // highlight-end 64 | }) 65 | ``` 66 | 67 | Once the test has been completed, you must restore the fake using the `container.restore` method. 68 | 69 | ```ts 70 | app.container.restore(UserService) 71 | 72 | // Restore UserService and PostService 73 | app.container.restoreAll([UserService, PostService]) 74 | 75 | // Restore all 76 | app.container.restoreAll() 77 | ``` 78 | 79 | ## Mocks and stubs using Sinon.js 80 | 81 | [Sinon](https://sinonjs.org/#get-started) is a mature, time-tested mocking library in the Node.js ecosystem. If you use mocks and stubs regularly, we recommend using Sinon, as it works great with TypeScript. 82 | 83 | ## Mocking network requests 84 | 85 | If your application makes outgoing HTTP requests to third-party services, you can use [nock](https://github.com/nock/nock) during testing to mock the network requests. 86 | 87 | Since nock intercepts all outgoing requests from the [Node.js HTTP module](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#class-httpclientrequest), it works with almost every third-party library like `got`, `axios` and so on. 88 | 89 | ## Freezing time 90 | You may use the [timekeeper](https://www.npmjs.com/package/timekeeper) package to freeze or travel time when writing tests. The timekeeper packages works by mocking the `Date` class. 91 | 92 | In the following example, we encapsulate the API of `timekeeper` inside a [Japa Test resource](https://japa.dev/docs/test-resources). 93 | 94 | ```ts 95 | import { getActiveTest } from '@japa/runner' 96 | import timekeeper from 'timekeeper' 97 | 98 | export function timeTravel(secondsToTravel: number) { 99 | const test = getActiveTest() 100 | if (!test) { 101 | throw new Error('Cannot use "timeTravel" outside of a Japa test') 102 | } 103 | 104 | timekeeper.reset() 105 | 106 | const date = new Date() 107 | date.setSeconds(date.getSeconds() + secondsToTravel) 108 | timekeeper.travel(date) 109 | 110 | test.cleanup(() => { 111 | timekeeper.reset() 112 | }) 113 | } 114 | ``` 115 | 116 | You may use the `timeTravel` method inside your tests as follows. 117 | 118 | ```ts 119 | import { timeTravel } from '#test_helpers' 120 | 121 | test('expire token after 2 hours', async ({ assert }) => { 122 | /** 123 | * Travel 3 hours into the future 124 | */ 125 | timeTravel(60 * 60 * 3) 126 | }) 127 | ``` 128 | -------------------------------------------------------------------------------- /content/docs/views-and-templates/edgejs.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Learn how to use Edge.js for templating in AdonisJS 3 | --- 4 | 5 | # EdgeJS 6 | 7 | Edge is a **simple**, **Modern**, and **batteries included** template engine created and maintained by the AdonisJS core team for Node.js. Edge is similar to writing JavaScript. If you know JavaScript, you know Edge. 8 | 9 | :::note 10 | The documentation for Edge is available on [https://edgejs.dev](https://edgejs.dev) 11 | ::: 12 | 13 | ## Installation 14 | 15 | Install and configure Edge using the following command. 16 | 17 | ```sh 18 | node ace add edge 19 | ``` 20 | 21 | :::disclosure{title="See steps performed by the add command"} 22 | 23 | 1. Installs the `edge.js` package using the detected package manager. 24 | 25 | 2. Registers the following service provider inside the `adonisrc.ts` file. 26 | 27 | ```ts 28 | { 29 | providers: [ 30 | // ...other providers 31 | () => import('@adonisjs/core/providers/edge_provider') 32 | ] 33 | } 34 | ``` 35 | 36 | ::: 37 | 38 | ## Rendering your first template 39 | 40 | Once the configuration is completed, you can use Edge to render templates. Let's create a `welcome.edge` file inside the `resources/views` directory. 41 | 42 | ```sh 43 | node ace make:view welcome 44 | ``` 45 | 46 | Open the newly created file and write the following markup inside it. 47 | 48 | ```edge 49 | 50 | 51 | 52 | 53 | 54 | 55 |

56 | Hello world from {{ request.url() }} endpoint 57 |

58 | 59 | 60 | ``` 61 | 62 | Finally, let's register a route to render the template. 63 | 64 | ```ts 65 | import router from '@adonisjs/core/services/router' 66 | 67 | router.get('/', async ({ view }) => { 68 | return view.render('welcome') 69 | }) 70 | ``` 71 | 72 | You can also use the `router.on().render` method to render a template without assigning a callback to the route. 73 | 74 | ```ts 75 | router.on('/').render('welcome') 76 | ``` 77 | 78 | ### Passing data to the template 79 | 80 | You can pass data to the template by passing an object as the second argument to the `view.render` method. 81 | 82 | ```ts 83 | router.get('/', async ({ view }) => { 84 | return view.render('welcome', { username: 'romainlanz' }) 85 | }) 86 | ``` 87 | 88 | ## Configuring Edge 89 | You can use Edge plugins or add global helpers to Edge by creating a [preload file](../concepts/adonisrc_file.md#preloads) inside the `start` directory. 90 | 91 | ```sh 92 | node ace make:preload view 93 | ``` 94 | 95 | ```ts 96 | // title: start/view.ts 97 | import edge from 'edge.js' 98 | import env from '#start/env' 99 | import { edgeIconify } from 'edge-iconify' 100 | 101 | /** 102 | * Register a plugin 103 | */ 104 | edge.use(edgeIconify) 105 | 106 | /** 107 | * Define a global property 108 | */ 109 | edge.global('appUrl', env.get('APP_URL')) 110 | ``` 111 | 112 | ## Global helpers 113 | 114 | Please check the [Edge helpers reference guide](../references/edge.md) to view the list of helpers contributed by AdonisJS. 115 | 116 | ## Learn more 117 | 118 | - [Edge.js documentation](https://edgejs.dev) 119 | - [Components](https://edgejs.dev/docs/components/introduction) 120 | - [SVG icons](https://edgejs.dev/docs/edge-iconify) 121 | - [Adocasts Edge Series](https://adocasts.com/topics/edge) 122 | -------------------------------------------------------------------------------- /content/docs/views-and-templates/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | summary: Available options for rendering views and templates in AdonisJS 3 | --- 4 | 5 | # Views and Templates 6 | 7 | AdonisJS is an excellent fit for creating traditional server-rendered applications in Node.js. If you enjoy the simplicity of using a backend template engine that outputs HTML without any overhead of Virtual DOM and build tools, then this guide is for you. 8 | 9 | The typical workflow of a server-rendered application in AdonisJS looks as follows. 10 | 11 | - Choose a template engine to render HTML dynamically. 12 | - Use [Vite](../basics/vite.md) for bundling CSS and frontend JavaScript. 13 | - Optionally, you can opt for libraries like [HTMX](https://htmx.org/) or [Unpoly](https://unpoly.com/) to progressively enhance your application and navigate like a SPA. 14 | 15 | :::note 16 | The AdonisJS core team has created a framework-agnostic template engine called [Edge.js](https://edgejs.dev) but does not force you to use it. You can use any other template engine you would like inside an AdonisJS application. 17 | ::: 18 | 19 | ## Popular options 20 | 21 | Following is the list of popular template engines you can use inside an AdonisJS application (just like any other Node.js application). 22 | 23 | - [**EdgeJS**](https://edgejs.dev) is a simple, modern, and batteries included template engine created and maintained by the AdonisJS core team for Node.js. 24 | - [**Pug**](https://pugjs.org) is a template engine heavily influenced by Haml. 25 | - [**Nunjucks**](https://mozilla.github.io/nunjucks) is a rich feature template engine inspired by Jinja2. 26 | 27 | ## Hybrid applications 28 | 29 | AdonisJS is also a great fit for creating hybrid applications that render HTML on the server and then hydrate your JavaScript on the client. This approach is popular among developers who want to use `Vue`, `React`, `Svelte`, `Solid`, or others for building interactive user interfaces but still want a full backend stack to handle server-side concerns. 30 | 31 | In this case, AdonisJS provide a first-class support for using [InertiaJS](./inertia.md) to bridge the gap between your frontend and backend. 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adonisjs-web-stater-kit", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "license": "UNLICENSED", 7 | "scripts": { 8 | "export": "vite build && npm run download:sponsors && node --import=ts-node-maintained/register/esm bin/build.ts", 9 | "build": "npm run export", 10 | "postexport": "copyfiles -u 1 public/* public/**/* dist", 11 | "download:sponsors": "node --import=ts-node-maintained/register/esm bin/download_sponsors.ts", 12 | "serve": "node --import=ts-node-maintained/register/esm bin/serve.ts", 13 | "dev": "vite build && npm run serve", 14 | "test": "node ace test" 15 | }, 16 | "imports": { 17 | "#src/*": "./src/*.js" 18 | }, 19 | "devDependencies": { 20 | "@adonisjs/assembler": "^7.7.0", 21 | "@adonisjs/eslint-config": "^1.3.0", 22 | "@adonisjs/prettier-config": "^1.3.0", 23 | "@adonisjs/tsconfig": "^1.3.0", 24 | "@adonisjs/vite": "^3.0.0", 25 | "@alpinejs/collapse": "^3.14.1", 26 | "@alpinejs/persist": "^3.14.1", 27 | "@dimerapp/content": "^5.2.1", 28 | "@dimerapp/docs-theme": "^6.1.2", 29 | "@dimerapp/edge": "^5.0.0", 30 | "@dimerapp/markdown": "^8.0.1", 31 | "@docsearch/css": "^3.6.1", 32 | "@docsearch/js": "^3.6.1", 33 | "@swc/core": "^1.7.10", 34 | "@types/node": "^22.2.0", 35 | "alpinejs": "^3.14.1", 36 | "collect.js": "^4.36.1", 37 | "concurrently": "^8.2.2", 38 | "copyfiles": "^2.4.1", 39 | "eslint": "^8.57.0", 40 | "medium-zoom": "^1.1.0", 41 | "pino-pretty": "^11.2.2", 42 | "prettier": "^3.3.3", 43 | "reflect-metadata": "^0.2.2", 44 | "sharp": "^0.33.4", 45 | "ts-node-maintained": "^10.9.4", 46 | "typescript": "^5.5.4", 47 | "unpoly": "^3.8.0", 48 | "vite": "^5.4.0" 49 | }, 50 | "dependencies": { 51 | "@adonisjs/core": "^6.12.1", 52 | "@adonisjs/static": "^1.1.1", 53 | "@radix-ui/colors": "^3.0.0", 54 | "dayjs": "^1.11.12", 55 | "edge-uikit": "^1.0.0-1", 56 | "edge.js": "^6.0.2", 57 | "undici": "^6.19.7" 58 | }, 59 | "eslintConfig": { 60 | "extends": "@adonisjs/eslint-config/app" 61 | }, 62 | "prettier": "@adonisjs/prettier-config" 63 | } 64 | -------------------------------------------------------------------------------- /public/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/icons/favicon.ico -------------------------------------------------------------------------------- /public/icons/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /public/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /public/icons/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "AdonisJS", 3 | "name": "AdonisJS - Docs", 4 | "background_color": "#5a45ff", 5 | "display": "standalone", 6 | "theme_color": "#f9f9f9", 7 | "start_url": "/guides/context", 8 | "icons": [ 9 | { 10 | "src": "/icons/android-chrome-192x192.png", 11 | "sizes": "192x192", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "/icons/android-chrome-512x512.png", 16 | "sizes": "512x512", 17 | "type": "image/png" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /public/og/ace_args.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/ace_args.png -------------------------------------------------------------------------------- /public/og/ace_creating_commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/ace_creating_commands.png -------------------------------------------------------------------------------- /public/og/ace_flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/ace_flags.png -------------------------------------------------------------------------------- /public/og/ace_introduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/ace_introduction.png -------------------------------------------------------------------------------- /public/og/ace_prompts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/ace_prompts.png -------------------------------------------------------------------------------- /public/og/ace_tui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/ace_tui.png -------------------------------------------------------------------------------- /public/og/authentication_access_tokens_guard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/authentication_access_tokens_guard.png -------------------------------------------------------------------------------- /public/og/authentication_basic_auth_guard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/authentication_basic_auth_guard.png -------------------------------------------------------------------------------- /public/og/authentication_custom_auth_guard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/authentication_custom_auth_guard.png -------------------------------------------------------------------------------- /public/og/authentication_introduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/authentication_introduction.png -------------------------------------------------------------------------------- /public/og/authentication_session_guard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/authentication_session_guard.png -------------------------------------------------------------------------------- /public/og/authentication_social_authentication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/authentication_social_authentication.png -------------------------------------------------------------------------------- /public/og/authentication_verifying_user_credentials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/authentication_verifying_user_credentials.png -------------------------------------------------------------------------------- /public/og/basics_body_parser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_body_parser.png -------------------------------------------------------------------------------- /public/og/basics_controllers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_controllers.png -------------------------------------------------------------------------------- /public/og/basics_cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_cookies.png -------------------------------------------------------------------------------- /public/og/basics_exception_handling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_exception_handling.png -------------------------------------------------------------------------------- /public/og/basics_file_uploads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_file_uploads.png -------------------------------------------------------------------------------- /public/og/basics_middleware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_middleware.png -------------------------------------------------------------------------------- /public/og/basics_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_request.png -------------------------------------------------------------------------------- /public/og/basics_response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_response.png -------------------------------------------------------------------------------- /public/og/basics_routing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_routing.png -------------------------------------------------------------------------------- /public/og/basics_session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_session.png -------------------------------------------------------------------------------- /public/og/basics_static_file_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_static_file_server.png -------------------------------------------------------------------------------- /public/og/basics_validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_validation.png -------------------------------------------------------------------------------- /public/og/basics_vite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/basics_vite.png -------------------------------------------------------------------------------- /public/og/concepts_adonisrc_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_adonisrc_file.png -------------------------------------------------------------------------------- /public/og/concepts_application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_application.png -------------------------------------------------------------------------------- /public/og/concepts_application_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_application_lifecycle.png -------------------------------------------------------------------------------- /public/og/concepts_assembler_hooks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_assembler_hooks.png -------------------------------------------------------------------------------- /public/og/concepts_async_local_storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_async_local_storage.png -------------------------------------------------------------------------------- /public/og/concepts_config_providers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_config_providers.png -------------------------------------------------------------------------------- /public/og/concepts_container_services.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_container_services.png -------------------------------------------------------------------------------- /public/og/concepts_dependency_injection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_dependency_injection.png -------------------------------------------------------------------------------- /public/og/concepts_extending_the_framework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_extending_the_framework.png -------------------------------------------------------------------------------- /public/og/concepts_hmr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_hmr.png -------------------------------------------------------------------------------- /public/og/concepts_http_context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_http_context.png -------------------------------------------------------------------------------- /public/og/concepts_http_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_http_overview.png -------------------------------------------------------------------------------- /public/og/concepts_scaffolding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_scaffolding.png -------------------------------------------------------------------------------- /public/og/concepts_service_providers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_service_providers.png -------------------------------------------------------------------------------- /public/og/concepts_tooling_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_tooling_config.png -------------------------------------------------------------------------------- /public/og/concepts_typescript_build_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/concepts_typescript_build_process.png -------------------------------------------------------------------------------- /public/og/database_introduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/database_introduction.png -------------------------------------------------------------------------------- /public/og/database_lucid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/database_lucid.png -------------------------------------------------------------------------------- /public/og/database_redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/database_redis.png -------------------------------------------------------------------------------- /public/og/digging_deeper_emitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/digging_deeper_emitter.png -------------------------------------------------------------------------------- /public/og/digging_deeper_i18n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/digging_deeper_i18n.png -------------------------------------------------------------------------------- /public/og/digging_deeper_locks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/digging_deeper_locks.png -------------------------------------------------------------------------------- /public/og/digging_deeper_logger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/digging_deeper_logger.png -------------------------------------------------------------------------------- /public/og/digging_deeper_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/digging_deeper_mail.png -------------------------------------------------------------------------------- /public/og/digging_deeper_repl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/digging_deeper_repl.png -------------------------------------------------------------------------------- /public/og/digging_deeper_transmit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/digging_deeper_transmit.png -------------------------------------------------------------------------------- /public/og/getting_started_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/getting_started_configuration.png -------------------------------------------------------------------------------- /public/og/getting_started_deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/getting_started_deployment.png -------------------------------------------------------------------------------- /public/og/getting_started_environment_variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/getting_started_environment_variables.png -------------------------------------------------------------------------------- /public/og/getting_started_folder_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/getting_started_folder_structure.png -------------------------------------------------------------------------------- /public/og/getting_started_installation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/getting_started_installation.png -------------------------------------------------------------------------------- /public/og/preface_contribution_guide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/preface_contribution_guide.png -------------------------------------------------------------------------------- /public/og/preface_faqs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/preface_faqs.png -------------------------------------------------------------------------------- /public/og/preface_governance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/preface_governance.png -------------------------------------------------------------------------------- /public/og/preface_introduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/preface_introduction.png -------------------------------------------------------------------------------- /public/og/preface_releases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/preface_releases.png -------------------------------------------------------------------------------- /public/og/references_commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/references_commands.png -------------------------------------------------------------------------------- /public/og/references_edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/references_edge.png -------------------------------------------------------------------------------- /public/og/references_events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/references_events.png -------------------------------------------------------------------------------- /public/og/references_exceptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/references_exceptions.png -------------------------------------------------------------------------------- /public/og/references_helpers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/references_helpers.png -------------------------------------------------------------------------------- /public/og/security_authorization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/security_authorization.png -------------------------------------------------------------------------------- /public/og/security_cors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/security_cors.png -------------------------------------------------------------------------------- /public/og/security_encryption.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/security_encryption.png -------------------------------------------------------------------------------- /public/og/security_hashing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/security_hashing.png -------------------------------------------------------------------------------- /public/og/security_rate_limiting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/security_rate_limiting.png -------------------------------------------------------------------------------- /public/og/security_securing_ssr_applications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/security_securing_ssr_applications.png -------------------------------------------------------------------------------- /public/og/testing_browser_tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/testing_browser_tests.png -------------------------------------------------------------------------------- /public/og/testing_console_tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/testing_console_tests.png -------------------------------------------------------------------------------- /public/og/testing_database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/testing_database.png -------------------------------------------------------------------------------- /public/og/testing_http_tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/testing_http_tests.png -------------------------------------------------------------------------------- /public/og/testing_introduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/testing_introduction.png -------------------------------------------------------------------------------- /public/og/testing_mocks_and_fakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/testing_mocks_and_fakes.png -------------------------------------------------------------------------------- /public/og/views_and-templates-edgejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/views_and-templates-edgejs.png -------------------------------------------------------------------------------- /public/og/views_and-templates-inertia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/views_and-templates-inertia.png -------------------------------------------------------------------------------- /public/og/views_and-templates-introduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adonisjs/v6-docs/HEAD/public/og/views_and-templates-introduction.png -------------------------------------------------------------------------------- /src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Bootstrap 4 | |-------------------------------------------------------------------------- 5 | | 6 | | The bootstrap file configures everything needed to render markdown with 7 | | extreme control over the rendering pipeline 8 | | 9 | */ 10 | 11 | import dayjs from 'dayjs' 12 | import edge from 'edge.js' 13 | import uiKit from 'edge-uikit' 14 | import collect from 'collect.js' 15 | import { dimer } from '@dimerapp/edge' 16 | import { readFile } from 'node:fs/promises' 17 | import { RenderingPipeline } from '@dimerapp/edge' 18 | import relativeTime from 'dayjs/plugin/relativeTime.js' 19 | import { Collection, Renderer } from '@dimerapp/content' 20 | import { docsHook, docsTheme } from '@dimerapp/docs-theme' 21 | 22 | import grammars from '../vscode_grammars/main.js' 23 | 24 | type CollectionEntry = Exclude, undefined> 25 | 26 | edge.use(dimer) 27 | edge.use(docsTheme) 28 | edge.use(uiKit) 29 | 30 | /** 31 | * Globally loads the config file 32 | */ 33 | edge.global('getConfig', async () => 34 | JSON.parse(await readFile(new URL('../content/config.json', import.meta.url), 'utf-8')) 35 | ) 36 | 37 | dayjs.extend(relativeTime) 38 | edge.global('dayjs', dayjs) 39 | 40 | /** 41 | * Globally loads the sponsors file 42 | */ 43 | edge.global('getSponsors', async () => 44 | JSON.parse(await readFile(new URL('../content/sponsors.json', import.meta.url), 'utf-8')) 45 | ) 46 | 47 | /** 48 | * Globally loads the releases file 49 | */ 50 | edge.global('getReleases', async () => 51 | JSON.parse(await readFile(new URL('../content/releases.json', import.meta.url), 'utf-8')) 52 | ) 53 | 54 | /** 55 | * Returns sections for a collection 56 | */ 57 | edge.global('getSections', function (collection: Collection, entry: CollectionEntry) { 58 | const entries = collection.all() 59 | 60 | return collect(entries) 61 | .groupBy('meta.category') 62 | .map((items, key) => { 63 | return { 64 | title: key, 65 | isActive: entry.meta.category === key, 66 | items: items 67 | .filter((item: CollectionEntry & { draft?: boolean }) => { 68 | return !item.meta.draft 69 | }) 70 | .map((item: CollectionEntry) => { 71 | return { 72 | href: item.permalink, 73 | title: item.title, 74 | isActive: item.permalink === entry.permalink, 75 | } 76 | }) 77 | .all(), 78 | } 79 | }) 80 | .all() 81 | }) 82 | 83 | edge.global('getPagination', function (collection: Collection, entry: CollectionEntry) { 84 | const entries = collection.all() 85 | const currentIndex = entries.findIndex((item) => item.permalink === entry.permalink) 86 | 87 | return { 88 | previous: { 89 | category: entries[currentIndex - 1]?.meta.category, 90 | title: entries[currentIndex - 1]?.title, 91 | url: entries[currentIndex - 1]?.permalink, 92 | }, 93 | next: { 94 | category: entries[currentIndex + 1]?.meta.category, 95 | title: entries[currentIndex + 1]?.title, 96 | url: entries[currentIndex + 1]?.permalink, 97 | }, 98 | } 99 | }) 100 | 101 | /** 102 | * Configuring rendering pipeline 103 | */ 104 | const pipeline = new RenderingPipeline() 105 | pipeline.use(docsHook).use((node) => { 106 | if (node.tagName === 'img') { 107 | return pipeline.component('elements/img', { node }) 108 | } 109 | }) 110 | 111 | // 'css-variables' | 'dark-plus' | 'dracula-soft' | 'dracula' | 'github-dark-dimmed' | 'github-dark' | 'github-light' | 'hc_light' | 'light-plus' | 'material-theme-darker' | 'material-theme-lighter' | 'material-theme-ocean' | 'material-theme-palenight' | 'material-theme' | 'min-dark' | 'min-light' | 'monokai' | 'nord' | 'one-dark-pro' | 'poimandres' | 'rose-pine-dawn' | 'rose-pine-moon' | 'rose-pine' | 'slack-dark' | 'slack-ochin' | 'solarized-dark' | 'solarized-light' | 'vitesse-dark' | 'vitesse-light'; 112 | 113 | /** 114 | * Configuring renderer 115 | */ 116 | export const renderer = new Renderer(edge, pipeline) 117 | .codeBlocksTheme('material-theme-darker') 118 | .useTemplate('docs') 119 | 120 | /** 121 | * Adding grammars 122 | */ 123 | grammars.forEach((grammar) => renderer.registerLanguage(grammar)) 124 | -------------------------------------------------------------------------------- /src/collections.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Collections 4 | |-------------------------------------------------------------------------- 5 | | 6 | | Collections represents multiple sources of documentation. For example: 7 | | Guides can be one collection, blog can be another, and API docs can 8 | | be another collection 9 | | 10 | */ 11 | 12 | import { Collection } from '@dimerapp/content' 13 | import { renderer } from './bootstrap.js' 14 | 15 | const docs = new Collection() 16 | .db(new URL('../content/docs/db.json', import.meta.url)) 17 | .useRenderer(renderer) 18 | .urlPrefix('/guides') 19 | .tap((entry) => { 20 | entry.setMarkdownOptions({ tocDepth: 3 }) 21 | entry.rendering((mdFile) => { 22 | mdFile.on('link', (node) => { 23 | if ( 24 | (node.url && 25 | !node.url.startsWith('http') && 26 | !node.url.startsWith('#') && 27 | !node.url.includes('.md')) || 28 | (node.url.includes('.md') && !node.url.startsWith('.')) 29 | ) { 30 | console.log(mdFile.filePath) 31 | console.log(node) 32 | } 33 | }) 34 | }) 35 | }) 36 | 37 | await docs.boot() 38 | 39 | export const collections = [docs] 40 | -------------------------------------------------------------------------------- /templates/authenticate.edge: -------------------------------------------------------------------------------- 1 | @let(siteConfig = await getConfig()) 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | @vite(['assets/app.css', 'assets/app.js']) 22 | @include('partials/detect_color_mode') 23 | 24 | 25 | 26 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /templates/docs.edge: -------------------------------------------------------------------------------- 1 | @component('layouts/main', { file }) 2 | @let(siteConfig = await getConfig()) 3 | 4 | @component('docs::header', siteConfig) 5 | @slot('logo') 6 | @include('partials/logo') 7 | @end 8 | 9 | @slot('logoMobile') 10 | @include('partials/logo_mobile') 11 | @end 12 | @end 13 | 14 |
15 | @!component('docs::sidebar', { 16 | collapsible: true, 17 | sections: getSections(collection, entry) 18 | }) 19 | 20 |
21 | @!component('docs::content_header', { title: file.frontmatter.title }) 22 | 23 | @component('docs::content', { 24 | title: file.frontmatter.title, 25 | copyright: siteConfig.copyright, 26 | fileEditUrl: `${siteConfig.fileEditBaseUrl}/${app.relativePath(file.filePath)}`, 27 | }) 28 | @!component('docs::doc_errors', { messages: file.messages }) 29 | @!component('dimer_contents', { nodes: file.ast.children, pipeline })~ 30 | @!component('partials/pagination', getPagination(collection, entry)) 31 | @end 32 | 33 | @if(file.toc) 34 | @component('docs::toc', { 35 | carbonAds: siteConfig.carbon_ads 36 | }) 37 | @!component('dimer_element', { node: file.toc, pipeline })~ 38 | @end 39 | @end 40 |
41 |
42 | @end 43 | -------------------------------------------------------------------------------- /templates/elements/img.edge: -------------------------------------------------------------------------------- 1 |
2 | @if(node.properties.src.startsWith('http://') || node.properties.src.startsWith('https://')) 3 | 4 | @else 5 | 6 | @end 7 |
8 | -------------------------------------------------------------------------------- /templates/layouts/main.edge: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | {{ file.frontmatter.title }} ({{ file.frontmatter.category }}) | AdonisJS Documentation 13 | @if(file.frontmatter.summary) 14 | 15 | @end 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | @if(file.frontmatter.summary) 31 | 32 | @end 33 | 34 | 35 | 36 | @if(file.frontmatter.summary) 37 | 38 | @end 39 | 40 | 41 | 42 | 43 | 47 | 48 | @vite(['assets/app.css', 'assets/app.js']) 49 | @include('partials/detect_color_mode') 50 | 51 | 52 | 53 | {{{ await $slots.main() }}} 54 | 55 | 56 | -------------------------------------------------------------------------------- /templates/logos/sponsors/galaxy.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /templates/logos/sponsors/toolzz.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /templates/partials/arrow.edge: -------------------------------------------------------------------------------- 1 | 2 | @if (direction === 'left') 3 | 4 | @else 5 | 6 | @endif 7 | 8 | -------------------------------------------------------------------------------- /templates/partials/detect_color_mode.edge: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /templates/partials/featured_sponsors.edge: -------------------------------------------------------------------------------- 1 | @let(featuredSponsors = (await getConfig()).featured_sponsors) 2 | 3 | @if(featuredSponsors) 4 |
5 |

Featured sponsors

6 | 7 |
8 | @each(sponsor in featuredSponsors) 9 | 16 | @end 17 | 18 | 25 |
26 |
27 | @end 28 | -------------------------------------------------------------------------------- /templates/partials/introduction_cards.edge: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | Getting started 5 |

6 | 7 | 41 |
42 | 43 |
44 |

45 | Community and Help 46 |

47 | 48 | 75 |
76 | 77 |
78 |

79 | Translations 80 |

81 | 82 |

83 | The following translations are not official and managed by the community 84 |

85 | 86 | 93 |
94 | 95 | {{--
96 |

97 | Insiders (Sponsorship) program 98 |

99 | 100 |

101 | Sponsorships make this project sustainable by buying the maintainer's time, which is a scarce resource. This time is spent maintaining the framework, adding new features, and creating new first-party packages everyone asks for. 102 |

103 | 104 |

105 | Click here to learn more about the Insiders program and the benefits associated with it. 106 |

107 | 108 |
109 | 110 | 111 |
112 |
--}} 113 |
114 | -------------------------------------------------------------------------------- /templates/partials/logo.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /templates/partials/logo_mobile.edge: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /templates/partials/pagination.edge: -------------------------------------------------------------------------------- 1 |
2 | 26 | -------------------------------------------------------------------------------- /templates/partials/recent_releases.edge: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | @each(release in (await getReleases()).slice(0, 5)) 12 | 13 | 18 | 21 | 24 | 25 | @end 26 | 27 |
PackageChangeReleased on
14 | 15 | {{ release.package }} 16 | 17 | 19 | {{{ release.description }}}. 20 | 22 | {{ dayjs(release.released_on).format('MMM D, YYYY') }} 23 |
28 |
29 | -------------------------------------------------------------------------------- /templates/partials/releases.edge: -------------------------------------------------------------------------------- 1 |

Releases

2 |

3 | This document outlines notable releases for the framework core and the ecosystem packages maintained by the AdonisJS core team. 4 |

5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | @each(release in await getReleases()) 17 | 18 | 23 | 26 | 29 | 30 | @end 31 | 32 |
PackageChangeReleased on
19 | 20 | {{ release.package }}@{{ release.version }} 21 | 22 | 24 | {{{ release.description }}}. 25 | 27 | {{ dayjs(release.released_on).format('MMM D, YYYY') }} 28 |
33 |
34 | -------------------------------------------------------------------------------- /templates/partials/sponsors.edge: -------------------------------------------------------------------------------- 1 | @let(sponsors = await getSponsors()) 2 | 3 |
4 |

5 | List of individuals and companies publicly sponsoring 6 | Harminder Virk on GitHub. 7 | If you are sponsoring and cannot see your name, ensure your 8 | sponsorship is not private. 9 |

10 | 11 | @include('partials/featured_sponsors') 12 | 13 | {{-- Rendering GitHub sponsors as per their selected tier --}} 14 | @!component('docs::elements/sponsors', { 15 | sponsors, 16 | tier: 'gold', 17 | title: 'Gold sponsors', 18 | finder: (sponsor) => { 19 | return !sponsor.isOneTime && sponsor.monthlyDollars > 29 20 | } 21 | }) 22 | 23 | @!component('docs::elements/sponsors', { 24 | sponsors, 25 | tier: 'silver', 26 | title: 'Silver sponsors', 27 | finder: (sponsor) => { 28 | return !sponsor.isOneTime && sponsor.monthlyDollars === 29 29 | } 30 | }) 31 | 32 | @!component('docs::elements/sponsors', { 33 | sponsors, 34 | tier: 'basic', 35 | title: 'Sponsors', 36 | finder: (sponsor) => { 37 | return !sponsor.isOneTime && sponsor.monthlyDollars >= 19 && sponsor.monthlyDollars < 29 38 | } 39 | }) 40 | 41 | @!component('docs::elements/sponsors', { 42 | sponsors, 43 | tier: 'basic', 44 | title: 'Backers', 45 | finder: (sponsor) => { 46 | return !sponsor.isOneTime && sponsor.monthlyDollars >= 0 && sponsor.monthlyDollars < 19 47 | } 48 | }) 49 | 50 | @!component('docs::elements/sponsors', { 51 | sponsors, 52 | tier: 'previous', 53 | title: 'Past sponsors', 54 | finder: (sponsor) => { 55 | return sponsor.monthlyDollars === -1 || sponsor.isOneTime 56 | } 57 | }) 58 |
59 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@adonisjs/tsconfig/tsconfig.app.json", 3 | "compilerOptions": { 4 | "rootDir": "./", 5 | "outDir": "./build" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import adonisjs from '@adonisjs/vite/client' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | adonisjs({ 7 | entrypoints: ['./assets/app.js', './assets/app.css'], 8 | reload: ['content/**/*', 'templates/**/*.edge'], 9 | }), 10 | ], 11 | }) 12 | -------------------------------------------------------------------------------- /vscode_grammars/dotenv.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DotENV", 3 | "scopeName": "source.env", 4 | "fileTypes": [ 5 | ".env", 6 | ".env-sample", 7 | ".env.example", 8 | ".env.local", 9 | ".env.dev", 10 | ".env.test", 11 | ".env.testing", 12 | ".env.production", 13 | ".env.prod" 14 | ], 15 | "uuid": "09d4e117-0975-453d-a74b-c2e525473f97", 16 | "patterns": [ 17 | { 18 | "comment": "Comments - starts with #", 19 | "match": "(#).*$\\n?", 20 | "name": "comment.line.number-sign.env", 21 | "captures": { 22 | "1": { 23 | "name": "punctuation.definition.comment.env" 24 | } 25 | } 26 | }, 27 | { 28 | "comment": "Strings (double)", 29 | "name": "string.quoted.double.env", 30 | "begin": "(\\\")", 31 | "beginCaptures": { 32 | "1": { 33 | "name": "punctuation.definition.string.begin.env" 34 | } 35 | }, 36 | "patterns": [ 37 | { 38 | "include": "#interpolation" 39 | }, 40 | { 41 | "include": "#variable" 42 | }, 43 | { 44 | "include": "#escape-characters" 45 | } 46 | ], 47 | "end": "(\\\")", 48 | "endCaptures": { 49 | "1": { 50 | "name": "punctuation.definition.string.end" 51 | } 52 | } 53 | }, 54 | { 55 | "comment": "Strings (single)", 56 | "name": "string.quoted.single.env", 57 | "begin": "(\\')", 58 | "beginCaptures": { 59 | "1": { 60 | "name": "punctuation.definition.string.begin.env" 61 | } 62 | }, 63 | "end": "(\\')", 64 | "endCaptures": { 65 | "1": { 66 | "name": "punctuation.definition.string.end" 67 | } 68 | } 69 | }, 70 | { 71 | "comment": "Assignment Operator", 72 | "match": "(?<=[\\w])\\s?=", 73 | "name": "keyword.operator.assignment.env" 74 | }, 75 | { 76 | "comment": "Variable", 77 | "match": "([\\w]+)(?=\\s?\\=)", 78 | "name": "variable.other.env" 79 | }, 80 | { 81 | "comment": "Keywords", 82 | "match": "(?i)\\s?(export)", 83 | "name": "keyword.other.env" 84 | }, 85 | { 86 | "comment": "Constants", 87 | "match": "(?i)(?<=\\=)\\s?(true|false|null)", 88 | "name": "constant.language.env" 89 | }, 90 | { 91 | "comment": "Numeric", 92 | "match": "\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)\\b", 93 | "name": "constant.numeric.env" 94 | } 95 | ], 96 | "repository": { 97 | "interpolation": { 98 | "comment": "Template Syntax: \"foo ${bar} {$baz}\"", 99 | "begin": "(\\$\\{|\\{)", 100 | "beginCaptures": { 101 | "1": { 102 | "name": "string.interpolated.env keyword.other.template.begin.env" 103 | } 104 | }, 105 | "patterns": [ 106 | { 107 | "match": "(?x)(\\$+)?([a-zA-Z_\\x{7f}-\\x{ff}][a-zA-Z0-9_\\x{7f}-\\x{ff}]*?\\b)", 108 | "captures": { 109 | "1": { 110 | "name": "punctuation.definition.variable.env variable.other.env" 111 | }, 112 | "2": { 113 | "name": "variable.other.env" 114 | } 115 | } 116 | } 117 | ], 118 | "end": "(\\})", 119 | "endCaptures": { 120 | "1": { 121 | "name": "string.interpolated.env keyword.other.template.end.env" 122 | } 123 | } 124 | }, 125 | "variable": { 126 | "patterns": [ 127 | { 128 | "match": "(?x)(\\$+)([a-zA-Z_\\x{7f}-\\x{ff}][a-zA-Z0-9_\\x{7f}-\\x{ff}]*?\\b)", 129 | "captures": { 130 | "1": { 131 | "name": "punctuation.definition.variable.env variable.other.env" 132 | }, 133 | "2": { 134 | "name": "variable.other.env" 135 | } 136 | } 137 | } 138 | ] 139 | }, 140 | "escape-characters": { 141 | "patterns": [ 142 | { 143 | "match": "\\\\[nrt\\\\\\$\\\"\\']", 144 | "name": "constant.character.escape.env" 145 | } 146 | ] 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /vscode_grammars/edge.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "Edge", 4 | "scopeName": "text.html.edge", 5 | "injections": { 6 | "text.html.edge - (meta.embedded | meta.tag | comment.block.edge), L:(text.html.edge meta.tag - (comment.block.edge | meta.embedded.block.edge)), L:(source.js.embedded.html - (comment.block.edge | meta.embedded.block.edge))": { 7 | "patterns": [ 8 | { 9 | "include": "#comment" 10 | }, 11 | { 12 | "include": "#escapedMustache" 13 | }, 14 | { 15 | "include": "#safeMustache" 16 | }, 17 | { 18 | "include": "#mustache" 19 | }, 20 | { 21 | "include": "#nonSeekableTag" 22 | }, 23 | { 24 | "include": "#tag" 25 | } 26 | ] 27 | } 28 | }, 29 | "repository": { 30 | "comment": { 31 | "begin": "\\{{--", 32 | "end": "\\--}}", 33 | "beginCaptures": { 34 | "0": { "name": "punctuation.definition.comment.begin.edge" } 35 | }, 36 | "endCaptures": { 37 | "0": { "name": "punctuation.definition.comment.end.edge" } 38 | }, 39 | "name": "comment.block" 40 | }, 41 | "escapedMustache": { 42 | "begin": "\\@{{", 43 | "end": "\\}}", 44 | "beginCaptures": { 45 | "0": { "name": "punctuation.definition.comment.begin.edge" } 46 | }, 47 | "endCaptures": { 48 | "0": { "name": "punctuation.definition.comment.end.edge" } 49 | }, 50 | "name": "comment.block" 51 | }, 52 | "safeMustache": { 53 | "begin": "\\{{{", 54 | "end": "\\}}}", 55 | "beginCaptures": { 56 | "0": { "name": "punctuation.mustache.begin" } 57 | }, 58 | "endCaptures": { 59 | "0": { "name": "punctuation.mustache.end" } 60 | }, 61 | "name": "meta.embedded.block.javascript", 62 | "patterns": [{ "include": "source.js#expression" }] 63 | }, 64 | "mustache": { 65 | "begin": "\\{{", 66 | "end": "\\}}", 67 | "beginCaptures": { 68 | "0": { "name": "punctuation.mustache.begin" } 69 | }, 70 | "endCaptures": { 71 | "0": { "name": "punctuation.mustache.end" } 72 | }, 73 | "name": "meta.embedded.block.javascript", 74 | "patterns": [{ "include": "source.js#expression" }] 75 | }, 76 | "nonSeekableTag": { 77 | "match": "^(\\s*)((@{1,2})(!)?([a-zA-Z._]+))(~)?$", 78 | "captures": { 79 | "2": { "name": "support.function.edge" } 80 | }, 81 | "name": "meta.embedded.block.javascript", 82 | "patterns": [{ "include": "source.js#expression" }] 83 | }, 84 | "tag": { 85 | "begin": "^(\\s*)((@{1,2})(!)?([a-zA-Z._]+)(\\s{0,2}))(\\()", 86 | "beginCaptures": { 87 | "2": { "name": "support.function.edge" }, 88 | "7": { "name": "punctuation.paren.open" } 89 | }, 90 | "end": "\\)", 91 | "endCaptures": { 92 | "0": { "name": "punctuation.paren.close" } 93 | }, 94 | "name": "meta.embedded.block.javascript", 95 | "patterns": [{ "include": "source.js#expression" }] 96 | } 97 | }, 98 | "patterns": [ 99 | { 100 | "include": "text.html.basic" 101 | }, 102 | { 103 | "include": "text.html.derivative" 104 | } 105 | ] 106 | } 107 | -------------------------------------------------------------------------------- /vscode_grammars/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | VSCode grammars 4 | |-------------------------------------------------------------------------- 5 | | 6 | | Export any custom VSCode languages from this file that you want to 7 | | use inside markdown codeblocks. 8 | | 9 | */ 10 | 11 | import { fileURLToPath } from 'node:url' 12 | import type { ILanguageRegistration } from '@dimerapp/shiki' 13 | 14 | export default [ 15 | { 16 | path: fileURLToPath(new URL('./dotenv.tmLanguage.json', import.meta.url)), 17 | scopeName: 'source.env', 18 | id: 'dotenv', 19 | }, 20 | { 21 | path: fileURLToPath(new URL('./edge.tmLanguage.json', import.meta.url)), 22 | scopeName: 'text.html.edge', 23 | id: 'edge', 24 | }, 25 | ] satisfies ILanguageRegistration[] 26 | --------------------------------------------------------------------------------