1import frontmatter from 'front-matter'; 2import fs from 'fs'; 3import path from 'path'; 4import { u as make } from 'unist-builder'; 5import { URL, fileURLToPath } from 'url'; 6 7import { VERSIONS } from './versions.js'; 8 9const dirname = path.dirname(fileURLToPath(import.meta.url)); 10const PAGES_DIR = path.resolve(dirname, '../pages'); 11 12// TODO(cedric): refactor docs to get rid of the directory lists 13 14/** Manual list of directories to categorize as "Home" */ 15const homeDirectories = [ 16 'get-started', 17 'develop', 18 'config-plugins', 19 'debugging', 20 'deploy', 21 'routing', 22]; 23/** Manual list of directories to categorize as "Learn" */ 24const learnDirectories = ['tutorial', 'ui-programming', 'additional-resources']; 25/** Manual list of directories to categorize as "Archive" */ 26const archiveDirectories = ['archive']; 27/** Manual list of directories to categorize as "Reference" */ 28const referenceDirectories = ['versions', 'technical-specs', 'more']; 29/** Private preview section which isn't linked in the documentation */ 30const previewDirectories = ['feature-preview', 'preview']; 31 32/** All other unlisted directories */ 33const generalDirectories = fs 34 .readdirSync(PAGES_DIR, { withFileTypes: true }) 35 .filter(entity => entity.isDirectory()) 36 .map(dir => dir.name) 37 .filter( 38 name => 39 name !== 'api' && 40 name !== 'versions' && 41 ![ 42 ...homeDirectories, 43 ...archiveDirectories, 44 ...referenceDirectories, 45 ...learnDirectories, 46 ...previewDirectories, 47 ].includes(name) 48 ); 49 50// --- Navigation --- 51 52const home = [ 53 makeSection('', [makePage('overview.mdx')]), 54 makeSection('Get started', [ 55 makePage('get-started/installation.mdx'), 56 makePage('get-started/expo-go.mdx'), 57 makePage('get-started/create-a-project.mdx'), 58 ]), 59 makeSection('Develop', [ 60 makePage('develop/project-structure.mdx'), 61 makeGroup( 62 'User interface', 63 [ 64 makePage('develop/user-interface/splash-screen.mdx'), 65 makePage('develop/user-interface/app-icons.mdx'), 66 makePage('develop/user-interface/safe-areas.mdx'), 67 makePage('develop/user-interface/fonts.mdx'), 68 makePage('develop/user-interface/color-themes.mdx'), 69 makePage('develop/user-interface/animation.mdx'), 70 makePage('develop/user-interface/store-data.mdx'), 71 makePage('develop/user-interface/next-steps.mdx'), 72 ], 73 { expanded: false } 74 ), 75 makeGroup( 76 'Routing', 77 [ 78 makePage('routing/introduction.mdx'), 79 makePage('routing/installation.mdx'), 80 makePage('routing/create-pages.mdx'), 81 makePage('routing/navigating-pages.mdx'), 82 makePage('routing/layouts.mdx'), 83 makePage('routing/appearance.mdx'), 84 makePage('routing/error-handling.mdx'), 85 makePage('routing/next-steps.mdx'), 86 ], 87 { expanded: false } 88 ), 89 makeGroup( 90 'Development builds', 91 [ 92 makePage('develop/development-builds/introduction.mdx'), 93 makePage('develop/development-builds/installation.mdx'), 94 makePage('develop/development-builds/create-a-build.mdx'), 95 makePage('develop/development-builds/use-development-builds.mdx'), 96 makePage('develop/development-builds/share-with-your-team.mdx'), 97 makePage('develop/development-builds/development-workflows.mdx'), 98 makePage('develop/development-builds/next-steps.mdx'), 99 ], 100 { expanded: false } 101 ), 102 makeGroup( 103 'Config plugins', 104 [ 105 makePage('config-plugins/introduction.mdx'), 106 makePage('config-plugins/plugins-and-mods.mdx'), 107 makePage('config-plugins/development-and-debugging.mdx'), 108 ], 109 { expanded: false } 110 ), 111 makeGroup( 112 'Debugging', 113 [ 114 makePage('debugging/errors-and-warnings.mdx'), 115 makePage('debugging/runtime-issues.mdx'), 116 makePage('debugging/tools.mdx'), 117 ], 118 { expanded: false } 119 ), 120 makePage('develop/authentication.mdx'), 121 makePage('develop/unit-testing.mdx'), 122 ]), 123 makeSection('Deploy', [ 124 makePage('deploy/build-project.mdx'), 125 makePage('deploy/submit-to-app-stores.mdx'), 126 makePage('deploy/app-stores-metadata.mdx'), 127 makePage('deploy/instant-updates.mdx'), 128 ]), 129 makeSection('More', [makePage('core-concepts.mdx'), makePage('faq.mdx')]), 130]; 131 132const general = [ 133 makeSection('', [makePage('guides/overview.mdx')]), 134 makeSection('Development process', [ 135 makePage('workflow/configuration.mdx'), 136 makePage('guides/local-app-development.mdx'), 137 makePage('workflow/using-libraries.mdx'), 138 makePage('guides/permissions.mdx'), 139 makePage('guides/environment-variables.mdx'), 140 makePage('guides/linking.mdx'), 141 makePage('guides/deep-linking.mdx'), 142 makeGroup( 143 'Custom native code', 144 [ 145 makePage('workflow/continuous-native-generation.mdx'), 146 makePage('workflow/customizing.mdx'), 147 makePage('workflow/prebuild.mdx'), 148 makePage('guides/adopting-prebuild.mdx'), 149 ], 150 { expanded: false } 151 ), 152 makeGroup( 153 'Web', 154 [ 155 makePage('workflow/web.mdx'), 156 makePage('guides/progressive-web-apps.mdx'), 157 makePage('distribution/publishing-websites.mdx'), 158 makePage('guides/customizing-metro.mdx'), 159 makePage('guides/customizing-webpack.mdx'), 160 makePage('guides/web-performance.mdx'), 161 ], 162 { expanded: false } 163 ), 164 makeGroup( 165 'Reference', 166 [ 167 makePage('guides/monorepos.mdx'), 168 makePage('workflow/logging.mdx'), 169 makePage('workflow/development-mode.mdx'), 170 makePage('workflow/common-development-errors.mdx'), 171 makePage('workflow/android-studio-emulator.mdx'), 172 makePage('workflow/ios-simulator.mdx'), 173 ], 174 { expanded: false } 175 ), 176 ]), 177 makeSection('Expo Router', [ 178 makeGroup('Advanced', [ 179 makePage('router/advanced/root-layout.mdx'), 180 makePage('router/advanced/stack.mdx'), 181 makePage('router/advanced/tabs.mdx'), 182 makePage('router/advanced/drawer.mdx'), 183 makePage('router/advanced/nesting-navigators.mdx'), 184 makePage('router/advanced/modals.mdx'), 185 makePage('router/advanced/platform-specific-modules.mdx'), 186 makePage('router/advanced/shared-routes.mdx'), 187 makePage('router/advanced/router-settings.mdx'), 188 makePage('router/advanced/apple-handoff.mdx'), 189 ]), 190 makeGroup('Reference', [ 191 makePage('router/reference/hooks.mdx'), 192 makePage('router/reference/search-parameters.mdx'), 193 makePage('router/reference/redirects.mdx'), 194 makePage('router/reference/static-rendering.mdx'), 195 makePage('router/reference/async-routes.mdx'), 196 makePage('router/reference/sitemap.mdx'), 197 makePage('router/reference/typed-routes.mdx'), 198 makePage('router/reference/authentication.mdx'), 199 makePage('router/reference/screen-tracking.mdx'), 200 makePage('router/reference/src-directory.mdx'), 201 makePage('router/reference/testing.mdx'), 202 makePage('router/reference/troubleshooting.mdx'), 203 makePage('router/reference/faq.mdx'), 204 ]), 205 makeGroup('Migration', [makePage('router/migrate/from-react-navigation.mdx')]), 206 ]), 207 makeSection( 208 'Expo Modules API', 209 [ 210 makePage('modules/overview.mdx'), 211 makePage('modules/get-started.mdx'), 212 makeSection('Tutorials', [ 213 makePage('modules/native-module-tutorial.mdx'), 214 makePage('modules/native-view-tutorial.mdx'), 215 makePage('modules/config-plugin-and-native-module-tutorial.mdx'), 216 makePage('modules/use-standalone-expo-module-in-your-project.mdx'), 217 makePage('modules/third-party-library.mdx'), 218 makePage('modules/existing-library.mdx'), 219 ]), 220 makeSection('Reference', [ 221 makePage('modules/module-api.mdx'), 222 makePage('modules/android-lifecycle-listeners.mdx'), 223 makePage('modules/appdelegate-subscribers.mdx'), 224 makePage('modules/autolinking.mdx'), 225 makePage('modules/module-config.mdx'), 226 ]), 227 ], 228 { expanded: false } 229 ), 230 makeSection('EAS', [makePage('eas/index.mdx'), makePage('eas/json.mdx')]), 231 makeSection('EAS Build', [ 232 makePage('build/introduction.mdx'), 233 makePage('build/setup.mdx'), 234 makePage('build/eas-json.mdx'), 235 makePage('build/internal-distribution.mdx'), 236 makePage('build/automate-submissions.mdx'), 237 makePage('build/updates.mdx'), 238 makePage('build/building-on-ci.mdx'), 239 makePage('build/building-from-github.mdx'), 240 makeGroup( 241 'App Signing', 242 [ 243 makePage('app-signing/app-credentials.mdx'), 244 makePage('app-signing/managed-credentials.mdx'), 245 makePage('app-signing/local-credentials.mdx'), 246 makePage('app-signing/existing-credentials.mdx'), 247 makePage('app-signing/syncing-credentials.mdx'), 248 makePage('app-signing/security.mdx'), 249 ], 250 { expanded: false } 251 ), 252 makeGroup( 253 'Custom builds', 254 [ 255 makePage('custom-builds/get-started.mdx'), 256 makePage('custom-builds/schema.mdx'), 257 makePage('custom-builds/functions.mdx'), 258 ], 259 { expanded: false } 260 ), 261 makeGroup( 262 'Reference', 263 [ 264 makePage('build-reference/migrating.mdx'), 265 makePage('build-reference/npm-hooks.mdx'), 266 makePage('build-reference/private-npm-packages.mdx'), 267 makePage('build-reference/git-submodules.mdx'), 268 makePage('build-reference/npm-cache-with-yarn.mdx'), 269 makePage('build-reference/build-with-monorepos.mdx'), 270 makePage('build-reference/variables.mdx'), 271 makePage('build-reference/apk.mdx'), 272 makePage('build-reference/simulators.mdx'), 273 makePage('build-reference/app-versions.mdx'), 274 makePage('build-reference/troubleshooting.mdx'), 275 makePage('build-reference/variants.mdx'), 276 makePage('build-reference/ios-capabilities.mdx'), 277 makePage('build-reference/local-builds.mdx'), 278 makePage('build-reference/caching.mdx'), 279 makePage('build-reference/android-builds.mdx'), 280 makePage('build-reference/ios-builds.mdx'), 281 makePage('build-reference/build-configuration.mdx'), 282 makePage('build-reference/infrastructure.mdx'), 283 makePage('build-reference/app-extensions.mdx'), 284 makePage('build-reference/e2e-tests.mdx'), 285 makePage('build-reference/limitations.mdx'), 286 ], 287 { expanded: false } 288 ), 289 ]), 290 makeSection('EAS Submit', [ 291 makePage('submit/introduction.mdx'), 292 makePage('submit/eas-json.mdx'), 293 makePage('submit/android.mdx'), 294 makePage('submit/ios.mdx'), 295 ]), 296 makeSection('EAS Update', [ 297 makePage('eas-update/introduction.mdx'), 298 makePage('eas-update/getting-started.mdx'), 299 makePage('eas-update/github-actions.mdx'), 300 makePage('eas-update/eas-cli.mdx'), 301 makePage('eas-update/develop-faster.mdx'), 302 makeGroup('Concepts', [ 303 makePage('eas-update/how-it-works.mdx'), 304 makePage('eas-update/runtime-versions.mdx'), 305 makePage('eas-update/deployment-patterns.mdx'), 306 ]), 307 makeGroup('Troubleshoot', [ 308 makePage('eas-update/debug.mdx'), 309 makePage('eas-update/debug-advanced.mdx'), 310 makePage('eas-update/expo-dev-client.mdx'), 311 makePage('eas-update/build-locally.mdx'), 312 ]), 313 makeGroup('Advanced', [ 314 makePage('eas-update/optimize-assets.mdx'), 315 makePage('eas-update/environment-variables.mdx'), 316 makePage('eas-update/code-signing.mdx'), 317 makePage('eas-update/rollouts.mdx'), 318 ]), 319 makeGroup('Reference', [ 320 makePage('eas-update/migrate-from-classic-updates.mdx'), 321 makePage('eas-update/codepush.mdx'), 322 makePage('eas-update/updating-your-app.mdx'), 323 makePage('eas-update/faq.mdx'), 324 makePage('eas-update/known-issues.mdx'), 325 ]), 326 ]), 327 makeSection('EAS Metadata', [ 328 makePage('eas/metadata/index.mdx'), 329 makePage('eas/metadata/getting-started.mdx'), 330 makeGroup( 331 'Reference', 332 [ 333 makePage('eas/metadata/config.mdx'), 334 makePage('eas/metadata/schema.mdx'), 335 makePage('eas/metadata/faq.mdx'), 336 ], 337 { expanded: false } 338 ), 339 ]), 340 makeSection('EAS Insights', [makePage('eas-insights/introduction.mdx')]), 341 makeSection('Push notifications', [ 342 makePage('push-notifications/overview.mdx'), 343 makePage('push-notifications/push-notifications-setup.mdx'), 344 makePage('push-notifications/sending-notifications.mdx'), 345 makePage('push-notifications/receiving-notifications.mdx'), 346 makeGroup( 347 'Reference', 348 [ 349 makePage('push-notifications/sending-notifications-custom.mdx'), 350 makePage('push-notifications/faq.mdx'), 351 ], 352 { expanded: false } 353 ), 354 ]), 355 makeSection('Distribution', [ 356 makePage('distribution/introduction.mdx'), 357 makePage('distribution/app-stores.mdx'), 358 makePage('distribution/runtime-versions.mdx'), 359 makePage('distribution/custom-updates-server.mdx'), 360 makePage('distribution/app-transfers.mdx'), 361 ]), 362 makeSection( 363 'More', 364 [ 365 makePage('workflow/upgrading-expo-sdk-walkthrough.mdx'), 366 makePage('eas/webhooks.mdx'), 367 makeSection('Assorted', [ 368 makePage('guides/authentication.mdx'), 369 makePage('guides/troubleshooting-proxies.mdx'), 370 makePage('guides/sharing-preview-releases.mdx'), 371 makePage('guides/using-hermes.mdx'), 372 makePage('guides/ios-developer-mode.mdx'), 373 makePage('guides/icons.mdx'), 374 makePage('guides/localization.mdx'), 375 makePage('guides/configuring-js-engines.mdx'), 376 ]), 377 makeSection('Integrations', [ 378 makePage('guides/using-analytics.mdx'), 379 makePage('guides/facebook-authentication.mdx'), 380 makePage('guides/using-firebase.mdx'), 381 makePage('guides/using-flipper.mdx'), 382 makePage('guides/google-authentication.mdx'), 383 makePage('guides/using-eslint.mdx'), 384 makePage('guides/using-nextjs.mdx'), 385 makePage('guides/using-sentry.mdx'), 386 makePage('guides/typescript.mdx'), 387 ]), 388 makeSection('Expo accounts', [ 389 makePage('accounts/account-types.mdx'), 390 makePage('accounts/two-factor.mdx'), 391 makePage('accounts/programmatic-access.mdx'), 392 makePage('accounts/working-together.mdx'), 393 makePage('accounts/sso.mdx'), 394 ]), 395 makeSection('Bare React Native', [ 396 makePage('bare/overview.mdx'), 397 makePage('bare/installing-expo-modules.mdx'), 398 makePage('bare/using-expo-cli.mdx'), 399 makePage('bare/installing-updates.mdx'), 400 makePage('bare/using-expo-client.mdx'), 401 makePage('bare/install-dev-builds-in-bare.mdx'), 402 makePage('bare/error-recovery.mdx'), 403 ]), 404 ], 405 { expanded: true } 406 ), 407 makeSection('Regulatory compliance', [ 408 makePage('regulatory-compliance/data-and-privacy-protection.mdx'), 409 makePage('regulatory-compliance/gdpr.mdx'), 410 makePage('regulatory-compliance/hipaa.mdx'), 411 makePage('regulatory-compliance/privacy-shield.mdx'), 412 ]), 413]; 414 415const learn = [ 416 makeSection( 417 'Get started', 418 [ 419 makePage('tutorial/introduction.mdx'), 420 makePage('tutorial/create-your-first-app.mdx'), 421 makePage('tutorial/build-a-screen.mdx'), 422 makePage('tutorial/image-picker.mdx'), 423 makePage('tutorial/create-a-modal.mdx'), 424 makePage('tutorial/gestures.mdx'), 425 makePage('tutorial/screenshot.mdx'), 426 makePage('tutorial/platform-differences.mdx'), 427 makePage('tutorial/configuration.mdx'), 428 makePage('tutorial/follow-up.mdx'), 429 ], 430 { expanded: true } 431 ), 432 makeSection( 433 'UI programming', 434 [ 435 makePage('ui-programming/image-background.mdx'), 436 makePage('ui-programming/implementing-a-checkbox.mdx'), 437 makePage('ui-programming/z-index.mdx'), 438 makePage('ui-programming/using-svgs.mdx'), 439 makePage('ui-programming/react-native-toast.mdx'), 440 makePage('ui-programming/react-native-styling-buttons.mdx'), 441 makePage('ui-programming/user-interface-libraries.mdx'), 442 ], 443 { expanded: true } 444 ), 445 makeSection('More', [makePage('additional-resources/index.mdx')]), 446]; 447 448const preview = [ 449 makeSection('Preview', [ 450 makePage('preview/introduction.mdx'), 451 makePage('preview/support.mdx'), 452 { expanded: true }, 453 makeSection('Expo Router', [makePage('preview/api-routes.mdx')]), 454 ]), 455]; 456 457const archive = [ 458 makeSection('Classic Builds', [ 459 makePage('archive/classic-updates/building-standalone-apps.mdx'), 460 makePage('archive/classic-updates/turtle-cli.mdx'), 461 ]), 462 makeSection('Classic Updates', [ 463 makePage('archive/classic-updates/introduction.mdx'), 464 makeSection('Guides', [ 465 makePage('archive/classic-updates/configuring-updates.mdx'), 466 makePage('archive/classic-updates/preloading-and-caching-assets.mdx'), 467 ]), 468 makeSection('Distribution', [ 469 makePage('archive/classic-updates/release-channels.mdx'), 470 makePage('archive/classic-updates/advanced-release-channels.mdx'), 471 makePage('archive/classic-updates/hosting-your-app.mdx'), 472 makePage('archive/classic-updates/offline-support.mdx'), 473 makePage('archive/classic-updates/optimizing-updates.mdx'), 474 ]), 475 makeSection('Workflow', [makePage('archive/classic-updates/publishing.mdx')]), 476 makeSection('Bare Workflow', [makePage('archive/classic-updates/updating-your-app.mdx')]), 477 ]), 478 makeSection('Technical Specs', [makePage('archive/technical-specs/expo-updates-0.mdx')]), 479 makeSection('More', [ 480 makePage('archive/expo-cli.mdx'), 481 makePage('archive/managed-vs-bare.mdx'), 482 makePage('archive/notification-channels.mdx'), 483 makePage('archive/glossary.mdx'), 484 ]), 485]; 486 487const featurePreview = []; 488 489const versionsReference = VERSIONS.reduce( 490 (all, version) => ({ 491 ...all, 492 [version]: [ 493 makeSection('Configuration files', pagesFromDir(`versions/${version}/config`), { 494 expanded: true, 495 }), 496 makeSection('Expo SDK', pagesFromDir(`versions/${version}/sdk`), { expanded: true }), 497 makeSection('Technical specs', [ 498 makePage('technical-specs/expo-updates-1.mdx'), 499 makePage('technical-specs/expo-sfv-0.mdx'), 500 ]), 501 makeSection('More', [makePage('more/expo-cli.mdx'), makePage('more/glossary-of-terms.mdx')], { 502 expanded: true, 503 }), 504 makeSection( 505 'React Native', 506 [ 507 make('page', { 508 href: 'https://reactnative.dev/docs/components-and-apis', 509 sidebarTitle: 'Visit documentation', 510 }), 511 ], 512 { expanded: true } 513 ), 514 ], 515 }), 516 {} 517); 518 519const reference = { ...versionsReference, latest: versionsReference['latest'] }; 520 521export default { 522 home, 523 general, 524 learn, 525 preview, 526 archive, 527 featurePreview, 528 reference, 529 generalDirectories, 530 previewDirectories, 531 referenceDirectories, 532 archiveDirectories, 533 homeDirectories, 534 learnDirectories, 535}; 536 537// --- MDX methods --- 538 539function makeSection(name, children = [], props = {}) { 540 return make('section', { name, ...{ expanded: false, ...props } }, children); 541} 542 543function makeGroup(name, children = [], props = {}) { 544 return make('group', { name, ...props }, children); 545} 546 547/** 548 * Parse the MDX page and extract the frontmatter/yaml page information. 549 * It will only look for the frontmatter/yaml block in the root nodes. 550 * This requires the `remark-frontmatter` MDX plugin. 551 * 552 * @param {string} file 553 */ 554function makePage(file) { 555 const filePath = path.resolve(PAGES_DIR, file); 556 const contents = fs.readFileSync(filePath, 'utf-8'); 557 const url = pageUrl(filePath); 558 const data = frontmatter(contents).attributes; 559 560 if (!data) { 561 console.error('Page YAML block is unreadable:', file); 562 } else if (!data.title) { 563 console.error('Page does not have a `title`:', file); 564 data.title = ''; 565 } 566 567 const result = { 568 // TODO(cedric): refactor name into title 569 name: data.title, 570 // TODO(cedric): refactor href into url 571 href: url, 572 }; 573 // TODO(cedric): refactor sidebarTitle into metadata 574 if (data.sidebar_title) { 575 result.sidebarTitle = data.sidebar_title; 576 } 577 // TODO(cedric): refactor hidden into `isHidden` and move it to metadata 578 if (data.hidden) { 579 result.hidden = data.hidden; 580 } 581 return make('page', result); 582} 583 584// --- Other helpers --- 585 586/** 587 * Load all pages from a single directory. 588 */ 589function pagesFromDir(dir) { 590 return fs 591 .readdirSync(path.resolve(PAGES_DIR, dir), { withFileTypes: true }) 592 .filter(entity => entity.isFile()) 593 .map(file => makePage(path.join(dir, file.name))); 594} 595 596/** 597 * Create the page url using the absolute file path. 598 * This parses the URL, relatively from PAGES_DIR. 599 * It also strips the file extension, and name if its `index`. 600 * These urls are pathnames, without trailing slashes. 601 */ 602function pageUrl(file) { 603 const filePath = path.parse(file); 604 const { pathname } = new URL(path.relative(PAGES_DIR, file), 'https://docs.expo.dev'); 605 return pathname 606 .replace(filePath.base, filePath.name === 'index' ? '' : filePath.name) 607 .replace(/\/$/, ''); 608} 609