This is page 132 of 141. Use http://codebase.md/xmlui-org/xmlui/tools/vscode/resources/%7BimageSrc%7D?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ └── config.json
├── .eslintrc.cjs
├── .github
│ ├── build-checklist.png
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows
│ ├── deploy-blog.yml
│ ├── deploy-docs-optimized.yml
│ ├── deploy-docs.yml
│ ├── prepare-versions.yml
│ ├── release-packages.yml
│ ├── run-all-tests.yml
│ └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│ ├── launch.json
│ └── settings.json
├── blog
│ ├── .gitignore
│ ├── .gitkeep
│ ├── CHANGELOG.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── layout-changes.md
│ ├── package.json
│ ├── public
│ │ ├── blog
│ │ │ ├── images
│ │ │ │ ├── blog-page-component.png
│ │ │ │ ├── blog-scrabble.png
│ │ │ │ ├── integrated-blog-search.png
│ │ │ │ └── lorem-ipsum.png
│ │ │ ├── lorem-ipsum.md
│ │ │ ├── newest-post.md
│ │ │ ├── older-post.md
│ │ │ └── welcome-to-the-xmlui-blog.md
│ │ ├── mockServiceWorker.js
│ │ ├── resources
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ └── for-download
│ │ │ │ └── xmlui
│ │ │ │ └── xmlui-standalone.umd.js
│ │ │ ├── github.svg
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ ├── rss.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ └── PageNotFound.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── blog-theme.ts
│ └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── ComponentRefLinks.txt
│ ├── content
│ │ ├── _meta.json
│ │ ├── components
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── APICall.md
│ │ │ ├── App.md
│ │ │ ├── AppHeader.md
│ │ │ ├── AppState.md
│ │ │ ├── AutoComplete.md
│ │ │ ├── Avatar.md
│ │ │ ├── Backdrop.md
│ │ │ ├── Badge.md
│ │ │ ├── BarChart.md
│ │ │ ├── Bookmark.md
│ │ │ ├── Breakout.md
│ │ │ ├── Button.md
│ │ │ ├── Card.md
│ │ │ ├── Carousel.md
│ │ │ ├── ChangeListener.md
│ │ │ ├── Checkbox.md
│ │ │ ├── CHStack.md
│ │ │ ├── ColorPicker.md
│ │ │ ├── Column.md
│ │ │ ├── ContentSeparator.md
│ │ │ ├── CVStack.md
│ │ │ ├── DataSource.md
│ │ │ ├── DateInput.md
│ │ │ ├── DatePicker.md
│ │ │ ├── DonutChart.md
│ │ │ ├── DropdownMenu.md
│ │ │ ├── EmojiSelector.md
│ │ │ ├── ExpandableItem.md
│ │ │ ├── FileInput.md
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FlowLayout.md
│ │ │ ├── Footer.md
│ │ │ ├── Form.md
│ │ │ ├── FormItem.md
│ │ │ ├── FormSection.md
│ │ │ ├── Fragment.md
│ │ │ ├── H1.md
│ │ │ ├── H2.md
│ │ │ ├── H3.md
│ │ │ ├── H4.md
│ │ │ ├── H5.md
│ │ │ ├── H6.md
│ │ │ ├── Heading.md
│ │ │ ├── HSplitter.md
│ │ │ ├── HStack.md
│ │ │ ├── Icon.md
│ │ │ ├── IFrame.md
│ │ │ ├── Image.md
│ │ │ ├── Items.md
│ │ │ ├── LabelList.md
│ │ │ ├── Legend.md
│ │ │ ├── LineChart.md
│ │ │ ├── Link.md
│ │ │ ├── List.md
│ │ │ ├── Logo.md
│ │ │ ├── Markdown.md
│ │ │ ├── MenuItem.md
│ │ │ ├── MenuSeparator.md
│ │ │ ├── ModalDialog.md
│ │ │ ├── NavGroup.md
│ │ │ ├── NavLink.md
│ │ │ ├── NavPanel.md
│ │ │ ├── NoResult.md
│ │ │ ├── NumberBox.md
│ │ │ ├── Option.md
│ │ │ ├── Page.md
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── Pages.md
│ │ │ ├── Pagination.md
│ │ │ ├── PasswordInput.md
│ │ │ ├── PieChart.md
│ │ │ ├── ProgressBar.md
│ │ │ ├── Queue.md
│ │ │ ├── RadioGroup.md
│ │ │ ├── RealTimeAdapter.md
│ │ │ ├── Redirect.md
│ │ │ ├── Select.md
│ │ │ ├── Slider.md
│ │ │ ├── Slot.md
│ │ │ ├── SpaceFiller.md
│ │ │ ├── Spinner.md
│ │ │ ├── Splitter.md
│ │ │ ├── Stack.md
│ │ │ ├── StickyBox.md
│ │ │ ├── SubMenuItem.md
│ │ │ ├── Switch.md
│ │ │ ├── TabItem.md
│ │ │ ├── Table.md
│ │ │ ├── TableOfContents.md
│ │ │ ├── Tabs.md
│ │ │ ├── Text.md
│ │ │ ├── TextArea.md
│ │ │ ├── TextBox.md
│ │ │ ├── Theme.md
│ │ │ ├── TimeInput.md
│ │ │ ├── Timer.md
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneSwitch.md
│ │ │ ├── Tooltip.md
│ │ │ ├── Tree.md
│ │ │ ├── VSplitter.md
│ │ │ ├── VStack.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ ├── xmlui-spreadsheet
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Spreadsheet.md
│ │ │ └── xmlui-website-blocks
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Carousel.md
│ │ │ ├── HelloMd.md
│ │ │ ├── HeroSection.md
│ │ │ └── ScrollToTop.md
│ │ └── extensions
│ │ ├── _meta.json
│ │ ├── xmlui-animations
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ ├── Animation.md
│ │ │ ├── FadeAnimation.md
│ │ │ ├── FadeInAnimation.md
│ │ │ ├── FadeOutAnimation.md
│ │ │ ├── ScaleAnimation.md
│ │ │ └── SlideInAnimation.md
│ │ └── xmlui-website-blocks
│ │ ├── _meta.json
│ │ ├── _overview.md
│ │ ├── Carousel.md
│ │ ├── HelloMd.md
│ │ ├── HeroSection.md
│ │ └── ScrollToTop.md
│ ├── extensions.ts
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ ├── public
│ │ ├── feed.rss
│ │ ├── mockServiceWorker.js
│ │ ├── pages
│ │ │ ├── _meta.json
│ │ │ ├── app-structure.md
│ │ │ ├── build-editor-component.md
│ │ │ ├── build-hello-world-component.md
│ │ │ ├── components-intro.md
│ │ │ ├── context-variables.md
│ │ │ ├── forms.md
│ │ │ ├── globals.md
│ │ │ ├── glossary.md
│ │ │ ├── helper-tags.md
│ │ │ ├── hosted-deployment.md
│ │ │ ├── howto
│ │ │ │ ├── assign-a-complex-json-literal-to-a-component-variable.md
│ │ │ │ ├── chain-a-refetch.md
│ │ │ │ ├── debug-a-component.md
│ │ │ │ ├── delay-a-datasource-until-another-datasource-is-ready.md
│ │ │ │ ├── delegate-a-method.md
│ │ │ │ ├── do-custom-form-validation.md
│ │ │ │ ├── expose-a-method-from-a-component.md
│ │ │ │ ├── filter-and-transform-data-from-an-api.md
│ │ │ │ ├── group-items-in-list-by-a-property.md
│ │ │ │ ├── handle-background-operations.md
│ │ │ │ ├── hide-an-element-until-its-datasource-is-ready.md
│ │ │ │ ├── make-a-set-of-equal-width-cards.md
│ │ │ │ ├── make-a-table-responsive.md
│ │ │ │ ├── make-navpanel-width-responsive.md
│ │ │ │ ├── modify-a-value-reported-in-a-column.md
│ │ │ │ ├── paginate-a-list.md
│ │ │ │ ├── pass-data-to-a-modal-dialog.md
│ │ │ │ ├── react-to-button-click-not-keystrokes.md
│ │ │ │ ├── set-the-initial-value-of-a-select-from-fetched-data.md
│ │ │ │ ├── share-a-modaldialog-across-components.md
│ │ │ │ ├── sync-selections-between-table-and-list-views.md
│ │ │ │ ├── update-ui-optimistically.md
│ │ │ │ ├── use-built-in-form-validation.md
│ │ │ │ └── use-the-same-modaldialog-to-add-or-edit.md
│ │ │ ├── howto.md
│ │ │ ├── intro.md
│ │ │ ├── layout.md
│ │ │ ├── markup.md
│ │ │ ├── mcp.md
│ │ │ ├── modal-dialogs.md
│ │ │ ├── news-and-reviews.md
│ │ │ ├── reactive-intro.md
│ │ │ ├── refactoring.md
│ │ │ ├── routing-and-links.md
│ │ │ ├── samples
│ │ │ │ ├── color-palette.xmlui
│ │ │ │ ├── color-values.xmlui
│ │ │ │ ├── shadow-sizes.xmlui
│ │ │ │ ├── spacing-sizes.xmlui
│ │ │ │ ├── swatch.xmlui
│ │ │ │ ├── theme-gallery-brief.xmlui
│ │ │ │ └── theme-gallery.xmlui
│ │ │ ├── scoping.md
│ │ │ ├── scripting.md
│ │ │ ├── styles-and-themes
│ │ │ │ ├── common-units.md
│ │ │ │ ├── layout-props.md
│ │ │ │ ├── theme-variable-defaults.md
│ │ │ │ ├── theme-variables.md
│ │ │ │ └── themes.md
│ │ │ ├── template-properties.md
│ │ │ ├── test.md
│ │ │ ├── tutorial-01.md
│ │ │ ├── tutorial-02.md
│ │ │ ├── tutorial-03.md
│ │ │ ├── tutorial-04.md
│ │ │ ├── tutorial-05.md
│ │ │ ├── tutorial-06.md
│ │ │ ├── tutorial-07.md
│ │ │ ├── tutorial-08.md
│ │ │ ├── tutorial-09.md
│ │ │ ├── tutorial-10.md
│ │ │ ├── tutorial-11.md
│ │ │ ├── tutorial-12.md
│ │ │ ├── universal-properties.md
│ │ │ ├── user-defined-components.md
│ │ │ ├── vscode.md
│ │ │ ├── working-with-markdown.md
│ │ │ ├── working-with-text.md
│ │ │ ├── xmlui-animations
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── Animation.md
│ │ │ │ ├── FadeAnimation.md
│ │ │ │ ├── FadeInAnimation.md
│ │ │ │ ├── FadeOutAnimation.md
│ │ │ │ ├── ScaleAnimation.md
│ │ │ │ └── SlideInAnimation.md
│ │ │ ├── xmlui-charts
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── DonutChart.md
│ │ │ │ ├── LabelList.md
│ │ │ │ ├── Legend.md
│ │ │ │ ├── LineChart.md
│ │ │ │ └── PieChart.md
│ │ │ ├── xmlui-pdf
│ │ │ │ ├── _meta.json
│ │ │ │ ├── _overview.md
│ │ │ │ └── Pdf.md
│ │ │ └── xmlui-spreadsheet
│ │ │ ├── _meta.json
│ │ │ ├── _overview.md
│ │ │ └── Spreadsheet.md
│ │ ├── resources
│ │ │ ├── devdocs
│ │ │ │ ├── debug-proxy-object-2.png
│ │ │ │ ├── debug-proxy-object.png
│ │ │ │ ├── table_editor_01.png
│ │ │ │ ├── table_editor_02.png
│ │ │ │ ├── table_editor_03.png
│ │ │ │ ├── table_editor_04.png
│ │ │ │ ├── table_editor_05.png
│ │ │ │ ├── table_editor_06.png
│ │ │ │ ├── table_editor_07.png
│ │ │ │ ├── table_editor_08.png
│ │ │ │ ├── table_editor_09.png
│ │ │ │ ├── table_editor_10.png
│ │ │ │ ├── table_editor_11.png
│ │ │ │ ├── table-editor-01.png
│ │ │ │ ├── table-editor-02.png
│ │ │ │ ├── table-editor-03.png
│ │ │ │ ├── table-editor-04.png
│ │ │ │ ├── table-editor-06.png
│ │ │ │ ├── table-editor-07.png
│ │ │ │ ├── table-editor-08.png
│ │ │ │ ├── table-editor-09.png
│ │ │ │ └── xmlui-rendering-of-tiptap-markdown.png
│ │ │ ├── favicon.ico
│ │ │ ├── files
│ │ │ │ ├── clients.json
│ │ │ │ ├── daily-revenue.json
│ │ │ │ ├── dashboard-stats.json
│ │ │ │ ├── demo.xmlui
│ │ │ │ ├── demo.xmlui.xs
│ │ │ │ ├── downloads
│ │ │ │ │ └── downloads.json
│ │ │ │ ├── for-download
│ │ │ │ │ ├── index-with-api.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── mockApi.js
│ │ │ │ │ ├── start-darwin.sh
│ │ │ │ │ ├── start-linux.sh
│ │ │ │ │ ├── start.bat
│ │ │ │ │ └── xmlui
│ │ │ │ │ └── xmlui-standalone.umd.js
│ │ │ │ ├── getting-started
│ │ │ │ │ ├── cl-tutorial-final.zip
│ │ │ │ │ ├── cl-tutorial.zip
│ │ │ │ │ ├── cl-tutorial2.zip
│ │ │ │ │ ├── cl-tutorial3.zip
│ │ │ │ │ ├── cl-tutorial4.zip
│ │ │ │ │ ├── cl-tutorial5.zip
│ │ │ │ │ ├── cl-tutorial6.zip
│ │ │ │ │ ├── getting-started.zip
│ │ │ │ │ ├── hello-xmlui.zip
│ │ │ │ │ ├── xmlui-empty.zip
│ │ │ │ │ └── xmlui-starter.zip
│ │ │ │ ├── howto
│ │ │ │ │ └── component-icons
│ │ │ │ │ └── up-arrow.svg
│ │ │ │ ├── invoices.json
│ │ │ │ ├── monthly-status.json
│ │ │ │ ├── news-and-reviews.json
│ │ │ │ ├── products.json
│ │ │ │ ├── releases.json
│ │ │ │ ├── tutorials
│ │ │ │ │ ├── datasource
│ │ │ │ │ │ └── api.ts
│ │ │ │ │ └── p2do
│ │ │ │ │ ├── api.ts
│ │ │ │ │ └── todo-logo.svg
│ │ │ │ └── xmlui.json
│ │ │ ├── github.svg
│ │ │ ├── images
│ │ │ │ ├── apiaction-tutorial
│ │ │ │ │ ├── add-success.png
│ │ │ │ │ ├── apiaction-param.png
│ │ │ │ │ ├── change-completed.png
│ │ │ │ │ ├── change-in-progress.png
│ │ │ │ │ ├── confirm-delete.png
│ │ │ │ │ ├── data-error.png
│ │ │ │ │ ├── data-progress.png
│ │ │ │ │ ├── data-success.png
│ │ │ │ │ ├── display-1.png
│ │ │ │ │ ├── item-deleted.png
│ │ │ │ │ ├── item-updated.png
│ │ │ │ │ ├── missing-api-key.png
│ │ │ │ │ ├── new-item-added.png
│ │ │ │ │ └── test-message.png
│ │ │ │ ├── chat-api
│ │ │ │ │ └── domain-model.svg
│ │ │ │ ├── components
│ │ │ │ │ ├── image
│ │ │ │ │ │ └── breakfast.jpg
│ │ │ │ │ ├── markdown
│ │ │ │ │ │ └── colors.png
│ │ │ │ │ └── modal
│ │ │ │ │ ├── deep_link_dialog_1.jpg
│ │ │ │ │ └── deep_link_dialog_2.jpg
│ │ │ │ ├── create-apps
│ │ │ │ │ ├── collapsed-vertical.png
│ │ │ │ │ ├── using-forms-warning-dialog.png
│ │ │ │ │ └── using-forms.png
│ │ │ │ ├── datasource-tutorial
│ │ │ │ │ ├── data-with-header.png
│ │ │ │ │ ├── filtered-data.png
│ │ │ │ │ ├── filtered-items.png
│ │ │ │ │ ├── initial-page-items.png
│ │ │ │ │ ├── list-items.png
│ │ │ │ │ ├── next-page-items.png
│ │ │ │ │ ├── no-data.png
│ │ │ │ │ ├── pagination-1.jpg
│ │ │ │ │ ├── pagination-1.png
│ │ │ │ │ ├── polling-1.png
│ │ │ │ │ ├── refetch-data.png
│ │ │ │ │ ├── slow-loading.png
│ │ │ │ │ ├── test-message.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── unconventional-data.png
│ │ │ │ │ └── unfiltered-items.png
│ │ │ │ ├── flower.jpg
│ │ │ │ ├── get-started
│ │ │ │ │ ├── add-new-contact.png
│ │ │ │ │ ├── app-modified.png
│ │ │ │ │ ├── app-start.png
│ │ │ │ │ ├── app-with-boxes.png
│ │ │ │ │ ├── app-with-toast.png
│ │ │ │ │ ├── boilerplate-structure.png
│ │ │ │ │ ├── cl-initial.png
│ │ │ │ │ ├── cl-start.png
│ │ │ │ │ ├── contact-counts.png
│ │ │ │ │ ├── contact-dialog-title.png
│ │ │ │ │ ├── contact-dialog.png
│ │ │ │ │ ├── contact-menus.png
│ │ │ │ │ ├── contact-predicates.png
│ │ │ │ │ ├── context-menu.png
│ │ │ │ │ ├── dashboard-numbers.png
│ │ │ │ │ ├── default-contact-list.png
│ │ │ │ │ ├── delete-contact.png
│ │ │ │ │ ├── delete-task.png
│ │ │ │ │ ├── detailed-template.png
│ │ │ │ │ ├── edit-contact-details.png
│ │ │ │ │ ├── edited-contact-saved.png
│ │ │ │ │ ├── empty-sections.png
│ │ │ │ │ ├── filter-completed.png
│ │ │ │ │ ├── fullwidth-desktop.png
│ │ │ │ │ ├── fullwidth-mobile.png
│ │ │ │ │ ├── initial-table.png
│ │ │ │ │ ├── items-and-badges.png
│ │ │ │ │ ├── loading-message.png
│ │ │ │ │ ├── new-contact-button.png
│ │ │ │ │ ├── new-contact-saved.png
│ │ │ │ │ ├── no-empty-sections.png
│ │ │ │ │ ├── personal-todo-initial.png
│ │ │ │ │ ├── piechart.png
│ │ │ │ │ ├── review-today.png
│ │ │ │ │ ├── rudimentary-dashboard.png
│ │ │ │ │ ├── section-collapsed.png
│ │ │ │ │ ├── sectioned-items.png
│ │ │ │ │ ├── sections-ordered.png
│ │ │ │ │ ├── spacex-list-with-links.png
│ │ │ │ │ ├── spacex-list.png
│ │ │ │ │ ├── start-personal-todo-1.png
│ │ │ │ │ ├── submit-new-contact.png
│ │ │ │ │ ├── submit-new-task.png
│ │ │ │ │ ├── syntax-highlighting.png
│ │ │ │ │ ├── table-with-badge.png
│ │ │ │ │ ├── template-with-card.png
│ │ │ │ │ ├── test-emulated-api.png
│ │ │ │ │ ├── Thumbs.db
│ │ │ │ │ ├── todo-logo.png
│ │ │ │ │ └── xmlui-tools.png
│ │ │ │ ├── HelloApp.png
│ │ │ │ ├── HelloApp2.png
│ │ │ │ ├── logos
│ │ │ │ │ ├── xmlui1.svg
│ │ │ │ │ ├── xmlui2.svg
│ │ │ │ │ ├── xmlui3.svg
│ │ │ │ │ ├── xmlui4.svg
│ │ │ │ │ ├── xmlui5.svg
│ │ │ │ │ ├── xmlui6.svg
│ │ │ │ │ └── xmlui7.svg
│ │ │ │ ├── pdf
│ │ │ │ │ └── dummy-pdf.jpg
│ │ │ │ ├── rendering-engine
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ ├── Component.svg
│ │ │ │ │ ├── CompoundComponent.svg
│ │ │ │ │ ├── RootComponent.svg
│ │ │ │ │ └── tree-with-containers.svg
│ │ │ │ ├── reviewers-guide
│ │ │ │ │ ├── AppEngine-flow.svg
│ │ │ │ │ └── incbutton-in-action.png
│ │ │ │ ├── tools
│ │ │ │ │ └── boilerplate-structure.png
│ │ │ │ ├── try.svg
│ │ │ │ ├── tutorial
│ │ │ │ │ ├── app-chat-history.png
│ │ │ │ │ ├── app-content-placeholder.png
│ │ │ │ │ ├── app-header-and-content.png
│ │ │ │ │ ├── app-links-channel-selected.png
│ │ │ │ │ ├── app-links-click.png
│ │ │ │ │ ├── app-navigation.png
│ │ │ │ │ ├── finished-ex01.png
│ │ │ │ │ ├── finished-ex02.png
│ │ │ │ │ ├── hello.png
│ │ │ │ │ ├── splash-screen-advanced.png
│ │ │ │ │ ├── splash-screen-after-click.png
│ │ │ │ │ ├── splash-screen-centered.png
│ │ │ │ │ ├── splash-screen-events.png
│ │ │ │ │ ├── splash-screen-expression.png
│ │ │ │ │ ├── splash-screen-reuse-after.png
│ │ │ │ │ ├── splash-screen-reuse-before.png
│ │ │ │ │ └── splash-screen.png
│ │ │ │ └── tutorial-01.png
│ │ │ ├── llms.txt
│ │ │ ├── logo-dark.svg
│ │ │ ├── logo.svg
│ │ │ ├── pg-popout.svg
│ │ │ └── xmlui-logo.svg
│ │ ├── serve.json
│ │ └── web.config
│ ├── scripts
│ │ ├── download-latest-xmlui.js
│ │ ├── generate-rss.js
│ │ ├── get-releases.js
│ │ └── utils.js
│ ├── src
│ │ ├── components
│ │ │ ├── BlogOverview.xmlui
│ │ │ ├── BlogPage.xmlui
│ │ │ ├── Boxes.xmlui
│ │ │ ├── Breadcrumb.xmlui
│ │ │ ├── ChangeLog.xmlui
│ │ │ ├── ColorPalette.xmlui
│ │ │ ├── DocumentLinks.xmlui
│ │ │ ├── DocumentPage.xmlui
│ │ │ ├── DocumentPageNoTOC.xmlui
│ │ │ ├── Icons.xmlui
│ │ │ ├── IncButton.xmlui
│ │ │ ├── IncButton2.xmlui
│ │ │ ├── NameValue.xmlui
│ │ │ ├── PageNotFound.xmlui
│ │ │ ├── PaletteItem.xmlui
│ │ │ ├── Palettes.xmlui
│ │ │ ├── SectionHeader.xmlui
│ │ │ ├── TBD.xmlui
│ │ │ ├── Test.xmlui
│ │ │ ├── ThemesIntro.xmlui
│ │ │ ├── ThousandThemes.xmlui
│ │ │ ├── TubeStops.xmlui
│ │ │ ├── TubeStops.xmlui.xs
│ │ │ └── TwoColumnCode.xmlui
│ │ ├── config.ts
│ │ ├── Main.xmlui
│ │ └── themes
│ │ ├── docs-theme.ts
│ │ ├── earthtone.ts
│ │ ├── xmlui-gray-on-default.ts
│ │ ├── xmlui-green-on-default.ts
│ │ └── xmlui-orange-on-default.ts
│ └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│ ├── xmlui-animations
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── Animation.tsx
│ │ │ ├── AnimationNative.tsx
│ │ │ ├── FadeAnimation.tsx
│ │ │ ├── FadeInAnimation.tsx
│ │ │ ├── FadeOutAnimation.tsx
│ │ │ ├── index.tsx
│ │ │ ├── ScaleAnimation.tsx
│ │ │ └── SlideInAnimation.tsx
│ │ └── tsconfig.json
│ ├── xmlui-devtools
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── devtools
│ │ │ │ ├── DevTools.tsx
│ │ │ │ ├── DevToolsNative.module.scss
│ │ │ │ ├── DevToolsNative.tsx
│ │ │ │ ├── ModalDialog.module.scss
│ │ │ │ ├── ModalDialog.tsx
│ │ │ │ ├── ModalVisibilityContext.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── editor
│ │ │ │ └── Editor.tsx
│ │ │ └── index.tsx
│ │ ├── tsconfig.json
│ │ └── vite.config-overrides.ts
│ ├── xmlui-hello-world
│ │ ├── .gitignore
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── HelloWorld.module.scss
│ │ │ ├── HelloWorld.tsx
│ │ │ ├── HelloWorldNative.tsx
│ │ │ └── index.tsx
│ │ └── tsconfig.json
│ ├── xmlui-os-frames
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── IPhoneFrame.module.scss
│ │ │ ├── IPhoneFrame.tsx
│ │ │ ├── MacOSAppFrame.module.scss
│ │ │ ├── MacOSAppFrame.tsx
│ │ │ ├── WindowsAppFrame.module.scss
│ │ │ └── WindowsAppFrame.tsx
│ │ └── tsconfig.json
│ ├── xmlui-pdf
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ ├── components
│ │ │ │ └── Pdf.xmlui
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── LazyPdfNative.tsx
│ │ │ ├── Pdf.module.scss
│ │ │ └── Pdf.tsx
│ │ └── tsconfig.json
│ ├── xmlui-playground
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── hooks
│ │ │ │ ├── usePlayground.ts
│ │ │ │ └── useToast.ts
│ │ │ ├── index.tsx
│ │ │ ├── playground
│ │ │ │ ├── Box.module.scss
│ │ │ │ ├── Box.tsx
│ │ │ │ ├── CodeSelector.tsx
│ │ │ │ ├── ConfirmationDialog.module.scss
│ │ │ │ ├── ConfirmationDialog.tsx
│ │ │ │ ├── Editor.tsx
│ │ │ │ ├── Header.module.scss
│ │ │ │ ├── Header.tsx
│ │ │ │ ├── Playground.tsx
│ │ │ │ ├── PlaygroundContent.module.scss
│ │ │ │ ├── PlaygroundContent.tsx
│ │ │ │ ├── PlaygroundNative.module.scss
│ │ │ │ ├── PlaygroundNative.tsx
│ │ │ │ ├── Preview.module.scss
│ │ │ │ ├── Preview.tsx
│ │ │ │ ├── Select.module.scss
│ │ │ │ ├── StandalonePlayground.tsx
│ │ │ │ ├── StandalonePlaygroundNative.module.scss
│ │ │ │ ├── StandalonePlaygroundNative.tsx
│ │ │ │ ├── ThemeSwitcher.module.scss
│ │ │ │ ├── ThemeSwitcher.tsx
│ │ │ │ ├── ToneSwitcher.tsx
│ │ │ │ ├── Tooltip.module.scss
│ │ │ │ ├── Tooltip.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── providers
│ │ │ │ ├── Toast.module.scss
│ │ │ │ └── ToastProvider.tsx
│ │ │ ├── state
│ │ │ │ └── store.ts
│ │ │ ├── themes
│ │ │ │ └── theme.ts
│ │ │ └── utils
│ │ │ └── helpers.ts
│ │ └── tsconfig.json
│ ├── xmlui-search
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Search.module.scss
│ │ │ └── Search.tsx
│ │ └── tsconfig.json
│ ├── xmlui-spreadsheet
│ │ ├── .gitignore
│ │ ├── demo
│ │ │ └── Main.xmlui
│ │ ├── index.html
│ │ ├── index.ts
│ │ ├── meta
│ │ │ └── componentsMetadata.ts
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── index.tsx
│ │ │ ├── Spreadsheet.tsx
│ │ │ └── SpreadsheetNative.tsx
│ │ └── tsconfig.json
│ └── xmlui-website-blocks
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── demo
│ │ ├── components
│ │ │ ├── HeroBackgroundBreakoutPage.xmlui
│ │ │ ├── HeroBackgroundsPage.xmlui
│ │ │ ├── HeroContentsPage.xmlui
│ │ │ ├── HeroTextAlignPage.xmlui
│ │ │ ├── HeroTextPage.xmlui
│ │ │ └── HeroTonesPage.xmlui
│ │ ├── Main.xmlui
│ │ └── themes
│ │ └── default.ts
│ ├── index.html
│ ├── index.ts
│ ├── meta
│ │ └── componentsMetadata.ts
│ ├── package.json
│ ├── public
│ │ └── resources
│ │ ├── building.jpg
│ │ └── xmlui-logo.svg
│ ├── src
│ │ ├── Carousel
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── FancyButton
│ │ │ ├── FancyButton.module.scss
│ │ │ ├── FancyButton.tsx
│ │ │ └── FancyButton.xmlui
│ │ ├── Hello
│ │ │ ├── Hello.tsx
│ │ │ ├── Hello.xmlui
│ │ │ └── Hello.xmlui.xs
│ │ ├── HeroSection
│ │ │ ├── HeroSection.module.scss
│ │ │ ├── HeroSection.tsx
│ │ │ └── HeroSectionNative.tsx
│ │ ├── index.tsx
│ │ ├── ScrollToTop
│ │ │ ├── ScrollToTop.module.scss
│ │ │ ├── ScrollToTop.tsx
│ │ │ └── ScrollToTopNative.tsx
│ │ └── vite-env.d.ts
│ └── tsconfig.json
├── README.md
├── tools
│ ├── codefence
│ │ └── xmlui-code-fence-docs.md
│ ├── create-app
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── create-app.ts
│ │ ├── helpers
│ │ │ ├── copy.ts
│ │ │ ├── get-pkg-manager.ts
│ │ │ ├── git.ts
│ │ │ ├── install.ts
│ │ │ ├── is-folder-empty.ts
│ │ │ ├── is-writeable.ts
│ │ │ ├── make-dir.ts
│ │ │ └── validate-pkg.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── templates
│ │ │ ├── default
│ │ │ │ └── ts
│ │ │ │ ├── gitignore
│ │ │ │ ├── index.html
│ │ │ │ ├── index.ts
│ │ │ │ ├── public
│ │ │ │ │ ├── mockServiceWorker.js
│ │ │ │ │ ├── resources
│ │ │ │ │ │ ├── favicon.ico
│ │ │ │ │ │ └── xmlui-logo.svg
│ │ │ │ │ └── serve.json
│ │ │ │ └── src
│ │ │ │ ├── components
│ │ │ │ │ ├── ApiAware.xmlui
│ │ │ │ │ ├── Home.xmlui
│ │ │ │ │ ├── IncButton.xmlui
│ │ │ │ │ └── PagePanel.xmlui
│ │ │ │ ├── config.ts
│ │ │ │ └── Main.xmlui
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── tsconfig.json
│ ├── create-xmlui-hello-world
│ │ ├── index.js
│ │ └── package.json
│ └── vscode
│ ├── .gitignore
│ ├── .vscode
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── .vscodeignore
│ ├── build.sh
│ ├── CHANGELOG.md
│ ├── esbuild.js
│ ├── eslint.config.mjs
│ ├── formatter-docs.md
│ ├── generate-test-sample.sh
│ ├── LICENSE.md
│ ├── package-lock.json
│ ├── package.json
│ ├── README.md
│ ├── resources
│ │ ├── xmlui-logo.png
│ │ └── xmlui-markup-syntax-highlighting.png
│ ├── src
│ │ ├── extension.ts
│ │ └── server.ts
│ ├── syntaxes
│ │ └── xmlui.tmLanguage.json
│ ├── test-samples
│ │ └── sample.xmlui
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
├── .gitignore
├── bin
│ ├── bootstrap.js
│ ├── build-lib.ts
│ ├── build.ts
│ ├── index.ts
│ ├── preview.ts
│ ├── start.ts
│ ├── vite-xmlui-plugin.ts
│ └── viteConfig.ts
├── CHANGELOG.md
├── conventions
│ ├── component-qa-checklist.md
│ ├── copilot-conventions.md
│ ├── create-xmlui-components.md
│ ├── mermaid.md
│ ├── testing-conventions.md
│ └── xmlui-in-a-nutshell.md
├── dev-docs
│ ├── accessibility.md
│ ├── build-system.md
│ ├── build-xmlui.md
│ ├── component-behaviors.md
│ ├── components-with-options.md
│ ├── containers.md
│ ├── data-operations.md
│ ├── glossary.md
│ ├── index.md
│ ├── next
│ │ ├── component-dev-guide.md
│ │ ├── configuration-management-enhancement-summary.md
│ │ ├── documentation-scripts-refactoring-complete-summary.md
│ │ ├── documentation-scripts-refactoring-plan.md
│ │ ├── duplicate-pattern-extraction-summary.md
│ │ ├── error-handling-standardization-summary.md
│ │ ├── generating-component-reference.md
│ │ ├── index.md
│ │ ├── logging-consistency-implementation-summary.md
│ │ ├── project-build.md
│ │ ├── project-structure.md
│ │ ├── theme-context.md
│ │ ├── tiptap-design-considerations.md
│ │ ├── working-with-code.md
│ │ ├── xmlui-runtime-architecture
│ │ └── xmlui-wcag-accessibility-report.md
│ ├── react-fundamentals.md
│ ├── release-method.md
│ ├── standalone-app.md
│ ├── ud-components.md
│ └── xmlui-repo.md
├── package.json
├── playwright.config.ts
├── scripts
│ ├── coverage-only.js
│ ├── e2e-test-summary.js
│ ├── generate-docs
│ │ ├── build-downloads-map.mjs
│ │ ├── build-pages-map.mjs
│ │ ├── components-config.json
│ │ ├── configuration-management.mjs
│ │ ├── constants.mjs
│ │ ├── create-theme-files.mjs
│ │ ├── DocsGenerator.mjs
│ │ ├── error-handling.mjs
│ │ ├── extensions-config.json
│ │ ├── folders.mjs
│ │ ├── generate-summary-files.mjs
│ │ ├── get-docs.mjs
│ │ ├── input-handler.mjs
│ │ ├── logger.mjs
│ │ ├── logging-standards.mjs
│ │ ├── MetadataProcessor.mjs
│ │ ├── pattern-utilities.mjs
│ │ └── utils.mjs
│ ├── get-langserver-metadata.mjs
│ ├── inline-links.mjs
│ └── README-e2e-summary.md
├── src
│ ├── abstractions
│ │ ├── _conventions.md
│ │ ├── ActionDefs.ts
│ │ ├── AppContextDefs.ts
│ │ ├── ComponentDefs.ts
│ │ ├── ContainerDefs.ts
│ │ ├── ExtensionDefs.ts
│ │ ├── FunctionDefs.ts
│ │ ├── RendererDefs.ts
│ │ ├── scripting
│ │ │ ├── BlockScope.ts
│ │ │ ├── Compilation.ts
│ │ │ ├── LogicalThread.ts
│ │ │ ├── LoopScope.ts
│ │ │ ├── modules.ts
│ │ │ ├── ScriptParserError.ts
│ │ │ ├── Token.ts
│ │ │ ├── TryScope.ts
│ │ │ └── TryScopeExp.ts
│ │ └── ThemingDefs.ts
│ ├── components
│ │ ├── _conventions.md
│ │ ├── abstractions.ts
│ │ ├── Accordion
│ │ │ ├── Accordion.md
│ │ │ ├── Accordion.module.scss
│ │ │ ├── Accordion.spec.ts
│ │ │ ├── Accordion.tsx
│ │ │ ├── AccordionContext.tsx
│ │ │ ├── AccordionItem.tsx
│ │ │ ├── AccordionItemNative.tsx
│ │ │ └── AccordionNative.tsx
│ │ ├── Animation
│ │ │ └── AnimationNative.tsx
│ │ ├── APICall
│ │ │ ├── APICall.md
│ │ │ ├── APICall.spec.ts
│ │ │ ├── APICall.tsx
│ │ │ └── APICallNative.tsx
│ │ ├── App
│ │ │ ├── App.md
│ │ │ ├── App.module.scss
│ │ │ ├── App.spec.ts
│ │ │ ├── App.tsx
│ │ │ ├── AppLayoutContext.ts
│ │ │ ├── AppNative.tsx
│ │ │ ├── AppStateContext.ts
│ │ │ ├── doc-resources
│ │ │ │ ├── condensed-sticky.xmlui
│ │ │ │ ├── condensed.xmlui
│ │ │ │ ├── horizontal-sticky.xmlui
│ │ │ │ ├── horizontal.xmlui
│ │ │ │ ├── vertical-full-header.xmlui
│ │ │ │ ├── vertical-sticky.xmlui
│ │ │ │ └── vertical.xmlui
│ │ │ ├── IndexerContext.ts
│ │ │ ├── LinkInfoContext.ts
│ │ │ ├── SearchContext.tsx
│ │ │ ├── Sheet.module.scss
│ │ │ └── Sheet.tsx
│ │ ├── AppHeader
│ │ │ ├── AppHeader.md
│ │ │ ├── AppHeader.module.scss
│ │ │ ├── AppHeader.spec.ts
│ │ │ ├── AppHeader.tsx
│ │ │ └── AppHeaderNative.tsx
│ │ ├── AppState
│ │ │ ├── AppState.md
│ │ │ ├── AppState.spec.ts
│ │ │ ├── AppState.tsx
│ │ │ └── AppStateNative.tsx
│ │ ├── AutoComplete
│ │ │ ├── AutoComplete.md
│ │ │ ├── AutoComplete.module.scss
│ │ │ ├── AutoComplete.spec.ts
│ │ │ ├── AutoComplete.tsx
│ │ │ ├── AutoCompleteContext.tsx
│ │ │ └── AutoCompleteNative.tsx
│ │ ├── Avatar
│ │ │ ├── Avatar.md
│ │ │ ├── Avatar.module.scss
│ │ │ ├── Avatar.spec.ts
│ │ │ ├── Avatar.tsx
│ │ │ └── AvatarNative.tsx
│ │ ├── Backdrop
│ │ │ ├── Backdrop.md
│ │ │ ├── Backdrop.module.scss
│ │ │ ├── Backdrop.spec.ts
│ │ │ ├── Backdrop.tsx
│ │ │ └── BackdropNative.tsx
│ │ ├── Badge
│ │ │ ├── Badge.md
│ │ │ ├── Badge.module.scss
│ │ │ ├── Badge.spec.ts
│ │ │ ├── Badge.tsx
│ │ │ └── BadgeNative.tsx
│ │ ├── Bookmark
│ │ │ ├── Bookmark.md
│ │ │ ├── Bookmark.module.scss
│ │ │ ├── Bookmark.spec.ts
│ │ │ ├── Bookmark.tsx
│ │ │ └── BookmarkNative.tsx
│ │ ├── Breakout
│ │ │ ├── Breakout.module.scss
│ │ │ ├── Breakout.spec.ts
│ │ │ ├── Breakout.tsx
│ │ │ └── BreakoutNative.tsx
│ │ ├── Button
│ │ │ ├── Button-style.spec.ts
│ │ │ ├── Button.md
│ │ │ ├── Button.module.scss
│ │ │ ├── Button.spec.ts
│ │ │ ├── Button.tsx
│ │ │ └── ButtonNative.tsx
│ │ ├── Card
│ │ │ ├── Card.md
│ │ │ ├── Card.module.scss
│ │ │ ├── Card.spec.ts
│ │ │ ├── Card.tsx
│ │ │ └── CardNative.tsx
│ │ ├── Carousel
│ │ │ ├── Carousel.md
│ │ │ ├── Carousel.module.scss
│ │ │ ├── Carousel.spec.ts
│ │ │ ├── Carousel.tsx
│ │ │ ├── CarouselContext.tsx
│ │ │ ├── CarouselItem.tsx
│ │ │ ├── CarouselItemNative.tsx
│ │ │ └── CarouselNative.tsx
│ │ ├── ChangeListener
│ │ │ ├── ChangeListener.md
│ │ │ ├── ChangeListener.spec.ts
│ │ │ ├── ChangeListener.tsx
│ │ │ └── ChangeListenerNative.tsx
│ │ ├── chart-color-schemes.ts
│ │ ├── Charts
│ │ │ ├── AreaChart
│ │ │ │ ├── AreaChart.md
│ │ │ │ ├── AreaChart.spec.ts
│ │ │ │ ├── AreaChart.tsx
│ │ │ │ └── AreaChartNative.tsx
│ │ │ ├── BarChart
│ │ │ │ ├── BarChart.md
│ │ │ │ ├── BarChart.module.scss
│ │ │ │ ├── BarChart.spec.ts
│ │ │ │ ├── BarChart.tsx
│ │ │ │ └── BarChartNative.tsx
│ │ │ ├── DonutChart
│ │ │ │ ├── DonutChart.spec.ts
│ │ │ │ └── DonutChart.tsx
│ │ │ ├── LabelList
│ │ │ │ ├── LabelList.spec.ts
│ │ │ │ ├── LabelList.tsx
│ │ │ │ ├── LabelListNative.module.scss
│ │ │ │ └── LabelListNative.tsx
│ │ │ ├── Legend
│ │ │ │ ├── Legend.spec.ts
│ │ │ │ ├── Legend.tsx
│ │ │ │ └── LegendNative.tsx
│ │ │ ├── LineChart
│ │ │ │ ├── LineChart.md
│ │ │ │ ├── LineChart.module.scss
│ │ │ │ ├── LineChart.spec.ts
│ │ │ │ ├── LineChart.tsx
│ │ │ │ └── LineChartNative.tsx
│ │ │ ├── PieChart
│ │ │ │ ├── PieChart.md
│ │ │ │ ├── PieChart.spec.ts
│ │ │ │ ├── PieChart.tsx
│ │ │ │ ├── PieChartNative.module.scss
│ │ │ │ └── PieChartNative.tsx
│ │ │ ├── RadarChart
│ │ │ │ ├── RadarChart.md
│ │ │ │ ├── RadarChart.spec.ts
│ │ │ │ ├── RadarChart.tsx
│ │ │ │ └── RadarChartNative.tsx
│ │ │ ├── Tooltip
│ │ │ │ ├── TooltipContent.module.scss
│ │ │ │ ├── TooltipContent.spec.ts
│ │ │ │ └── TooltipContent.tsx
│ │ │ └── utils
│ │ │ ├── abstractions.ts
│ │ │ └── ChartProvider.tsx
│ │ ├── Checkbox
│ │ │ ├── Checkbox.md
│ │ │ ├── Checkbox.spec.ts
│ │ │ └── Checkbox.tsx
│ │ ├── CodeBlock
│ │ │ ├── CodeBlock.module.scss
│ │ │ ├── CodeBlock.spec.ts
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── CodeBlockNative.tsx
│ │ │ └── highlight-code.ts
│ │ ├── collectedComponentMetadata.ts
│ │ ├── ColorPicker
│ │ │ ├── ColorPicker.md
│ │ │ ├── ColorPicker.module.scss
│ │ │ ├── ColorPicker.spec.ts
│ │ │ ├── ColorPicker.tsx
│ │ │ └── ColorPickerNative.tsx
│ │ ├── Column
│ │ │ ├── Column.md
│ │ │ ├── Column.tsx
│ │ │ ├── ColumnNative.tsx
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ └── TableContext.tsx
│ │ ├── component-utils.ts
│ │ ├── ComponentProvider.tsx
│ │ ├── ComponentRegistryContext.tsx
│ │ ├── container-helpers.tsx
│ │ ├── ContentSeparator
│ │ │ ├── ContentSeparator.md
│ │ │ ├── ContentSeparator.module.scss
│ │ │ ├── ContentSeparator.spec.ts
│ │ │ ├── ContentSeparator.tsx
│ │ │ └── ContentSeparatorNative.tsx
│ │ ├── DataSource
│ │ │ ├── DataSource.md
│ │ │ └── DataSource.tsx
│ │ ├── DateInput
│ │ │ ├── DateInput.md
│ │ │ ├── DateInput.module.scss
│ │ │ ├── DateInput.spec.ts
│ │ │ ├── DateInput.tsx
│ │ │ └── DateInputNative.tsx
│ │ ├── DatePicker
│ │ │ ├── DatePicker.md
│ │ │ ├── DatePicker.module.scss
│ │ │ ├── DatePicker.spec.ts
│ │ │ ├── DatePicker.tsx
│ │ │ └── DatePickerNative.tsx
│ │ ├── DropdownMenu
│ │ │ ├── DropdownMenu.md
│ │ │ ├── DropdownMenu.module.scss
│ │ │ ├── DropdownMenu.spec.ts
│ │ │ ├── DropdownMenu.tsx
│ │ │ ├── DropdownMenuNative.tsx
│ │ │ ├── MenuItem.md
│ │ │ └── SubMenuItem.md
│ │ ├── EmojiSelector
│ │ │ ├── EmojiSelector.md
│ │ │ ├── EmojiSelector.spec.ts
│ │ │ ├── EmojiSelector.tsx
│ │ │ └── EmojiSelectorNative.tsx
│ │ ├── ExpandableItem
│ │ │ ├── ExpandableItem.module.scss
│ │ │ ├── ExpandableItem.spec.ts
│ │ │ ├── ExpandableItem.tsx
│ │ │ └── ExpandableItemNative.tsx
│ │ ├── FileInput
│ │ │ ├── FileInput.md
│ │ │ ├── FileInput.module.scss
│ │ │ ├── FileInput.spec.ts
│ │ │ ├── FileInput.tsx
│ │ │ └── FileInputNative.tsx
│ │ ├── FileUploadDropZone
│ │ │ ├── FileUploadDropZone.md
│ │ │ ├── FileUploadDropZone.module.scss
│ │ │ ├── FileUploadDropZone.spec.ts
│ │ │ ├── FileUploadDropZone.tsx
│ │ │ └── FileUploadDropZoneNative.tsx
│ │ ├── FlowLayout
│ │ │ ├── FlowLayout.md
│ │ │ ├── FlowLayout.module.scss
│ │ │ ├── FlowLayout.spec.ts
│ │ │ ├── FlowLayout.spec.ts-snapshots
│ │ │ │ └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
│ │ │ ├── FlowLayout.tsx
│ │ │ └── FlowLayoutNative.tsx
│ │ ├── Footer
│ │ │ ├── Footer.md
│ │ │ ├── Footer.module.scss
│ │ │ ├── Footer.spec.ts
│ │ │ ├── Footer.tsx
│ │ │ └── FooterNative.tsx
│ │ ├── Form
│ │ │ ├── Form.md
│ │ │ ├── Form.module.scss
│ │ │ ├── Form.spec.ts
│ │ │ ├── Form.tsx
│ │ │ ├── formActions.ts
│ │ │ ├── FormContext.ts
│ │ │ └── FormNative.tsx
│ │ ├── FormItem
│ │ │ ├── FormItem.md
│ │ │ ├── FormItem.module.scss
│ │ │ ├── FormItem.spec.ts
│ │ │ ├── FormItem.tsx
│ │ │ ├── FormItemNative.tsx
│ │ │ ├── HelperText.module.scss
│ │ │ ├── HelperText.tsx
│ │ │ ├── ItemWithLabel.tsx
│ │ │ └── Validations.ts
│ │ ├── FormSection
│ │ │ ├── FormSection.md
│ │ │ ├── FormSection.ts
│ │ │ └── FormSection.xmlui
│ │ ├── Fragment
│ │ │ ├── Fragment.spec.ts
│ │ │ └── Fragment.tsx
│ │ ├── Heading
│ │ │ ├── abstractions.ts
│ │ │ ├── H1.md
│ │ │ ├── H1.spec.ts
│ │ │ ├── H2.md
│ │ │ ├── H2.spec.ts
│ │ │ ├── H3.md
│ │ │ ├── H3.spec.ts
│ │ │ ├── H4.md
│ │ │ ├── H4.spec.ts
│ │ │ ├── H5.md
│ │ │ ├── H5.spec.ts
│ │ │ ├── H6.md
│ │ │ ├── H6.spec.ts
│ │ │ ├── Heading.md
│ │ │ ├── Heading.module.scss
│ │ │ ├── Heading.spec.ts
│ │ │ ├── Heading.tsx
│ │ │ └── HeadingNative.tsx
│ │ ├── HoverCard
│ │ │ ├── HoverCard.tsx
│ │ │ └── HovercardNative.tsx
│ │ ├── HtmlTags
│ │ │ ├── HtmlTags.module.scss
│ │ │ ├── HtmlTags.spec.ts
│ │ │ └── HtmlTags.tsx
│ │ ├── Icon
│ │ │ ├── AdmonitionDanger.tsx
│ │ │ ├── AdmonitionInfo.tsx
│ │ │ ├── AdmonitionNote.tsx
│ │ │ ├── AdmonitionTip.tsx
│ │ │ ├── AdmonitionWarning.tsx
│ │ │ ├── ApiIcon.tsx
│ │ │ ├── ArrowDropDown.module.scss
│ │ │ ├── ArrowDropDown.tsx
│ │ │ ├── ArrowDropUp.module.scss
│ │ │ ├── ArrowDropUp.tsx
│ │ │ ├── ArrowLeft.module.scss
│ │ │ ├── ArrowLeft.tsx
│ │ │ ├── ArrowRight.module.scss
│ │ │ ├── ArrowRight.tsx
│ │ │ ├── Attach.tsx
│ │ │ ├── Binding.module.scss
│ │ │ ├── Binding.tsx
│ │ │ ├── BoardIcon.tsx
│ │ │ ├── BoxIcon.tsx
│ │ │ ├── CheckIcon.tsx
│ │ │ ├── ChevronDownIcon.tsx
│ │ │ ├── ChevronLeft.tsx
│ │ │ ├── ChevronRight.tsx
│ │ │ ├── ChevronUpIcon.tsx
│ │ │ ├── CodeFileIcon.tsx
│ │ │ ├── CodeSandbox.tsx
│ │ │ ├── CompactListIcon.tsx
│ │ │ ├── ContentCopyIcon.tsx
│ │ │ ├── DarkToLightIcon.tsx
│ │ │ ├── DatabaseIcon.module.scss
│ │ │ ├── DatabaseIcon.tsx
│ │ │ ├── DocFileIcon.tsx
│ │ │ ├── DocIcon.tsx
│ │ │ ├── DotMenuHorizontalIcon.tsx
│ │ │ ├── DotMenuIcon.tsx
│ │ │ ├── EmailIcon.tsx
│ │ │ ├── EmptyFolderIcon.tsx
│ │ │ ├── ErrorIcon.tsx
│ │ │ ├── ExpressionIcon.tsx
│ │ │ ├── FillPlusCricleIcon.tsx
│ │ │ ├── FilterIcon.tsx
│ │ │ ├── FolderIcon.tsx
│ │ │ ├── GlobeIcon.tsx
│ │ │ ├── HomeIcon.tsx
│ │ │ ├── HyperLinkIcon.tsx
│ │ │ ├── Icon.md
│ │ │ ├── Icon.module.scss
│ │ │ ├── Icon.spec.ts
│ │ │ ├── Icon.tsx
│ │ │ ├── IconNative.tsx
│ │ │ ├── ImageFileIcon.tsx
│ │ │ ├── Inspect.tsx
│ │ │ ├── LightToDark.tsx
│ │ │ ├── LinkIcon.tsx
│ │ │ ├── ListIcon.tsx
│ │ │ ├── LooseListIcon.tsx
│ │ │ ├── MoonIcon.tsx
│ │ │ ├── MoreOptionsIcon.tsx
│ │ │ ├── NoSortIcon.tsx
│ │ │ ├── PDFIcon.tsx
│ │ │ ├── PenIcon.tsx
│ │ │ ├── PhoneIcon.tsx
│ │ │ ├── PhotoIcon.tsx
│ │ │ ├── PlusIcon.tsx
│ │ │ ├── SearchIcon.tsx
│ │ │ ├── ShareIcon.tsx
│ │ │ ├── SortAscendingIcon.tsx
│ │ │ ├── SortDescendingIcon.tsx
│ │ │ ├── StarsIcon.tsx
│ │ │ ├── SunIcon.tsx
│ │ │ ├── svg
│ │ │ │ ├── admonition_danger.svg
│ │ │ │ ├── admonition_info.svg
│ │ │ │ ├── admonition_note.svg
│ │ │ │ ├── admonition_tip.svg
│ │ │ │ ├── admonition_warning.svg
│ │ │ │ ├── api.svg
│ │ │ │ ├── arrow-dropdown.svg
│ │ │ │ ├── arrow-left.svg
│ │ │ │ ├── arrow-right.svg
│ │ │ │ ├── arrow-up.svg
│ │ │ │ ├── attach.svg
│ │ │ │ ├── binding.svg
│ │ │ │ ├── box.svg
│ │ │ │ ├── bulb.svg
│ │ │ │ ├── code-file.svg
│ │ │ │ ├── code-sandbox.svg
│ │ │ │ ├── dark_to_light.svg
│ │ │ │ ├── database.svg
│ │ │ │ ├── doc.svg
│ │ │ │ ├── empty-folder.svg
│ │ │ │ ├── expression.svg
│ │ │ │ ├── eye-closed.svg
│ │ │ │ ├── eye-dark.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── file-text.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── folder.svg
│ │ │ │ ├── img.svg
│ │ │ │ ├── inspect.svg
│ │ │ │ ├── light_to_dark.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── photo.svg
│ │ │ │ ├── share.svg
│ │ │ │ ├── stars.svg
│ │ │ │ ├── sun.svg
│ │ │ │ ├── trending-down.svg
│ │ │ │ ├── trending-level.svg
│ │ │ │ ├── trending-up.svg
│ │ │ │ ├── txt.svg
│ │ │ │ ├── unknown-file.svg
│ │ │ │ ├── unlink.svg
│ │ │ │ └── xls.svg
│ │ │ ├── TableDeleteColumnIcon.tsx
│ │ │ ├── TableDeleteRowIcon.tsx
│ │ │ ├── TableInsertColumnIcon.tsx
│ │ │ ├── TableInsertRowIcon.tsx
│ │ │ ├── TrashIcon.tsx
│ │ │ ├── TrendingDownIcon.tsx
│ │ │ ├── TrendingLevelIcon.tsx
│ │ │ ├── TrendingUpIcon.tsx
│ │ │ ├── TxtIcon.tsx
│ │ │ ├── UnknownFileIcon.tsx
│ │ │ ├── UnlinkIcon.tsx
│ │ │ ├── UserIcon.tsx
│ │ │ ├── WarningIcon.tsx
│ │ │ └── XlsIcon.tsx
│ │ ├── IconProvider.tsx
│ │ ├── IconRegistryContext.tsx
│ │ ├── IFrame
│ │ │ ├── IFrame.md
│ │ │ ├── IFrame.module.scss
│ │ │ ├── IFrame.spec.ts
│ │ │ ├── IFrame.tsx
│ │ │ └── IFrameNative.tsx
│ │ ├── Image
│ │ │ ├── Image.md
│ │ │ ├── Image.module.scss
│ │ │ ├── Image.spec.ts
│ │ │ ├── Image.tsx
│ │ │ └── ImageNative.tsx
│ │ ├── Input
│ │ │ ├── index.ts
│ │ │ ├── InputAdornment.module.scss
│ │ │ ├── InputAdornment.tsx
│ │ │ ├── InputDivider.module.scss
│ │ │ ├── InputDivider.tsx
│ │ │ ├── InputLabel.module.scss
│ │ │ ├── InputLabel.tsx
│ │ │ ├── PartialInput.module.scss
│ │ │ └── PartialInput.tsx
│ │ ├── InspectButton
│ │ │ ├── InspectButton.module.scss
│ │ │ └── InspectButton.tsx
│ │ ├── Items
│ │ │ ├── Items.md
│ │ │ ├── Items.spec.ts
│ │ │ ├── Items.tsx
│ │ │ └── ItemsNative.tsx
│ │ ├── Link
│ │ │ ├── Link.md
│ │ │ ├── Link.module.scss
│ │ │ ├── Link.spec.ts
│ │ │ ├── Link.tsx
│ │ │ └── LinkNative.tsx
│ │ ├── List
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── List.md
│ │ │ ├── List.module.scss
│ │ │ ├── List.spec.ts
│ │ │ ├── List.tsx
│ │ │ └── ListNative.tsx
│ │ ├── Logo
│ │ │ ├── doc-resources
│ │ │ │ └── xmlui-logo.svg
│ │ │ ├── Logo.md
│ │ │ ├── Logo.tsx
│ │ │ └── LogoNative.tsx
│ │ ├── Markdown
│ │ │ ├── CodeText.module.scss
│ │ │ ├── CodeText.tsx
│ │ │ ├── Markdown.md
│ │ │ ├── Markdown.module.scss
│ │ │ ├── Markdown.spec.ts
│ │ │ ├── Markdown.tsx
│ │ │ ├── MarkdownNative.tsx
│ │ │ ├── parse-binding-expr.ts
│ │ │ └── utils.ts
│ │ ├── metadata-helpers.ts
│ │ ├── ModalDialog
│ │ │ ├── ConfirmationModalContextProvider.tsx
│ │ │ ├── Dialog.module.scss
│ │ │ ├── Dialog.tsx
│ │ │ ├── ModalDialog.md
│ │ │ ├── ModalDialog.module.scss
│ │ │ ├── ModalDialog.spec.ts
│ │ │ ├── ModalDialog.tsx
│ │ │ ├── ModalDialogNative.tsx
│ │ │ └── ModalVisibilityContext.tsx
│ │ ├── NavGroup
│ │ │ ├── NavGroup.md
│ │ │ ├── NavGroup.module.scss
│ │ │ ├── NavGroup.spec.ts
│ │ │ ├── NavGroup.tsx
│ │ │ ├── NavGroupContext.ts
│ │ │ └── NavGroupNative.tsx
│ │ ├── NavLink
│ │ │ ├── NavLink.md
│ │ │ ├── NavLink.module.scss
│ │ │ ├── NavLink.spec.ts
│ │ │ ├── NavLink.tsx
│ │ │ └── NavLinkNative.tsx
│ │ ├── NavPanel
│ │ │ ├── NavPanel.md
│ │ │ ├── NavPanel.module.scss
│ │ │ ├── NavPanel.spec.ts
│ │ │ ├── NavPanel.tsx
│ │ │ └── NavPanelNative.tsx
│ │ ├── NestedApp
│ │ │ ├── AppWithCodeView.module.scss
│ │ │ ├── AppWithCodeView.tsx
│ │ │ ├── AppWithCodeViewNative.tsx
│ │ │ ├── defaultProps.tsx
│ │ │ ├── logo.svg
│ │ │ ├── NestedApp.module.scss
│ │ │ ├── NestedApp.tsx
│ │ │ ├── NestedAppNative.tsx
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.tsx
│ │ │ └── utils.ts
│ │ ├── NoResult
│ │ │ ├── NoResult.md
│ │ │ ├── NoResult.module.scss
│ │ │ ├── NoResult.spec.ts
│ │ │ ├── NoResult.tsx
│ │ │ └── NoResultNative.tsx
│ │ ├── NumberBox
│ │ │ ├── numberbox-abstractions.ts
│ │ │ ├── NumberBox.md
│ │ │ ├── NumberBox.module.scss
│ │ │ ├── NumberBox.spec.ts
│ │ │ ├── NumberBox.tsx
│ │ │ └── NumberBoxNative.tsx
│ │ ├── Option
│ │ │ ├── Option.md
│ │ │ ├── Option.spec.ts
│ │ │ ├── Option.tsx
│ │ │ ├── OptionNative.tsx
│ │ │ └── OptionTypeProvider.tsx
│ │ ├── PageMetaTitle
│ │ │ ├── PageMetaTilteNative.tsx
│ │ │ ├── PageMetaTitle.md
│ │ │ ├── PageMetaTitle.spec.ts
│ │ │ └── PageMetaTitle.tsx
│ │ ├── Pages
│ │ │ ├── Page.md
│ │ │ ├── Pages.md
│ │ │ ├── Pages.module.scss
│ │ │ ├── Pages.tsx
│ │ │ └── PagesNative.tsx
│ │ ├── Pagination
│ │ │ ├── Pagination.md
│ │ │ ├── Pagination.module.scss
│ │ │ ├── Pagination.spec.ts
│ │ │ ├── Pagination.tsx
│ │ │ └── PaginationNative.tsx
│ │ ├── PositionedContainer
│ │ │ ├── PositionedContainer.module.scss
│ │ │ ├── PositionedContainer.tsx
│ │ │ └── PositionedContainerNative.tsx
│ │ ├── ProfileMenu
│ │ │ ├── ProfileMenu.module.scss
│ │ │ └── ProfileMenu.tsx
│ │ ├── ProgressBar
│ │ │ ├── ProgressBar.md
│ │ │ ├── ProgressBar.module.scss
│ │ │ ├── ProgressBar.spec.ts
│ │ │ ├── ProgressBar.tsx
│ │ │ └── ProgressBarNative.tsx
│ │ ├── Queue
│ │ │ ├── Queue.md
│ │ │ ├── Queue.spec.ts
│ │ │ ├── Queue.tsx
│ │ │ ├── queueActions.ts
│ │ │ └── QueueNative.tsx
│ │ ├── RadioGroup
│ │ │ ├── RadioGroup.md
│ │ │ ├── RadioGroup.module.scss
│ │ │ ├── RadioGroup.spec.ts
│ │ │ ├── RadioGroup.tsx
│ │ │ ├── RadioGroupNative.tsx
│ │ │ ├── RadioItem.tsx
│ │ │ └── RadioItemNative.tsx
│ │ ├── RealTimeAdapter
│ │ │ ├── RealTimeAdapter.tsx
│ │ │ └── RealTimeAdapterNative.tsx
│ │ ├── Redirect
│ │ │ ├── Redirect.md
│ │ │ ├── Redirect.spec.ts
│ │ │ └── Redirect.tsx
│ │ ├── ResponsiveBar
│ │ │ ├── README.md
│ │ │ ├── ResponsiveBar.md
│ │ │ ├── ResponsiveBar.module.scss
│ │ │ ├── ResponsiveBar.spec.ts
│ │ │ ├── ResponsiveBar.tsx
│ │ │ └── ResponsiveBarNative.tsx
│ │ ├── Select
│ │ │ ├── HiddenOption.tsx
│ │ │ ├── OptionContext.ts
│ │ │ ├── Select.md
│ │ │ ├── Select.module.scss
│ │ │ ├── Select.spec.ts
│ │ │ ├── Select.tsx
│ │ │ ├── SelectContext.tsx
│ │ │ └── SelectNative.tsx
│ │ ├── SelectionStore
│ │ │ ├── SelectionStore.md
│ │ │ ├── SelectionStore.tsx
│ │ │ └── SelectionStoreNative.tsx
│ │ ├── Slider
│ │ │ ├── Slider.md
│ │ │ ├── Slider.module.scss
│ │ │ ├── Slider.spec.ts
│ │ │ ├── Slider.tsx
│ │ │ └── SliderNative.tsx
│ │ ├── Slot
│ │ │ ├── Slot.md
│ │ │ ├── Slot.spec.ts
│ │ │ └── Slot.ts
│ │ ├── SlotItem.tsx
│ │ ├── SpaceFiller
│ │ │ ├── SpaceFiller.md
│ │ │ ├── SpaceFiller.module.scss
│ │ │ ├── SpaceFiller.spec.ts
│ │ │ ├── SpaceFiller.tsx
│ │ │ └── SpaceFillerNative.tsx
│ │ ├── Spinner
│ │ │ ├── Spinner.md
│ │ │ ├── Spinner.module.scss
│ │ │ ├── Spinner.spec.ts
│ │ │ ├── Spinner.tsx
│ │ │ └── SpinnerNative.tsx
│ │ ├── Splitter
│ │ │ ├── HSplitter.md
│ │ │ ├── HSplitter.spec.ts
│ │ │ ├── Splitter.md
│ │ │ ├── Splitter.module.scss
│ │ │ ├── Splitter.spec.ts
│ │ │ ├── Splitter.tsx
│ │ │ ├── SplitterNative.tsx
│ │ │ ├── utils.ts
│ │ │ ├── VSplitter.md
│ │ │ └── VSplitter.spec.ts
│ │ ├── Stack
│ │ │ ├── CHStack.md
│ │ │ ├── CHStack.spec.ts
│ │ │ ├── CVStack.md
│ │ │ ├── CVStack.spec.ts
│ │ │ ├── HStack.md
│ │ │ ├── HStack.spec.ts
│ │ │ ├── Stack.md
│ │ │ ├── Stack.module.scss
│ │ │ ├── Stack.spec.ts
│ │ │ ├── Stack.tsx
│ │ │ ├── StackNative.tsx
│ │ │ ├── VStack.md
│ │ │ └── VStack.spec.ts
│ │ ├── StickyBox
│ │ │ ├── StickyBox.md
│ │ │ ├── StickyBox.module.scss
│ │ │ ├── StickyBox.tsx
│ │ │ └── StickyBoxNative.tsx
│ │ ├── Switch
│ │ │ ├── Switch.md
│ │ │ ├── Switch.spec.ts
│ │ │ └── Switch.tsx
│ │ ├── Table
│ │ │ ├── doc-resources
│ │ │ │ └── list-component-data.js
│ │ │ ├── react-table-config.d.ts
│ │ │ ├── Table.md
│ │ │ ├── Table.module.scss
│ │ │ ├── Table.spec.ts
│ │ │ ├── Table.tsx
│ │ │ ├── TableNative.tsx
│ │ │ └── useRowSelection.tsx
│ │ ├── TableOfContents
│ │ │ ├── TableOfContents.module.scss
│ │ │ ├── TableOfContents.spec.ts
│ │ │ ├── TableOfContents.tsx
│ │ │ └── TableOfContentsNative.tsx
│ │ ├── Tabs
│ │ │ ├── TabContext.tsx
│ │ │ ├── TabItem.md
│ │ │ ├── TabItem.tsx
│ │ │ ├── TabItemNative.tsx
│ │ │ ├── Tabs.md
│ │ │ ├── Tabs.module.scss
│ │ │ ├── Tabs.spec.ts
│ │ │ ├── Tabs.tsx
│ │ │ └── TabsNative.tsx
│ │ ├── Text
│ │ │ ├── Text.md
│ │ │ ├── Text.module.scss
│ │ │ ├── Text.spec.ts
│ │ │ ├── Text.tsx
│ │ │ └── TextNative.tsx
│ │ ├── TextArea
│ │ │ ├── TextArea.md
│ │ │ ├── TextArea.module.scss
│ │ │ ├── TextArea.spec.ts
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextAreaNative.tsx
│ │ │ ├── TextAreaResizable.tsx
│ │ │ └── useComposedRef.ts
│ │ ├── TextBox
│ │ │ ├── TextBox.md
│ │ │ ├── TextBox.module.scss
│ │ │ ├── TextBox.spec.ts
│ │ │ ├── TextBox.tsx
│ │ │ └── TextBoxNative.tsx
│ │ ├── Theme
│ │ │ ├── NotificationToast.tsx
│ │ │ ├── Theme.md
│ │ │ ├── Theme.module.scss
│ │ │ ├── Theme.spec.ts
│ │ │ ├── Theme.tsx
│ │ │ └── ThemeNative.tsx
│ │ ├── TimeInput
│ │ │ ├── TimeInput.md
│ │ │ ├── TimeInput.module.scss
│ │ │ ├── TimeInput.spec.ts
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeInputNative.tsx
│ │ │ └── utils.ts
│ │ ├── Timer
│ │ │ ├── Timer.md
│ │ │ ├── Timer.spec.ts
│ │ │ ├── Timer.tsx
│ │ │ └── TimerNative.tsx
│ │ ├── Toggle
│ │ │ ├── Toggle.module.scss
│ │ │ └── Toggle.tsx
│ │ ├── ToneChangerButton
│ │ │ ├── ToneChangerButton.md
│ │ │ ├── ToneChangerButton.spec.ts
│ │ │ └── ToneChangerButton.tsx
│ │ ├── ToneSwitch
│ │ │ ├── ToneSwitch.md
│ │ │ ├── ToneSwitch.module.scss
│ │ │ ├── ToneSwitch.spec.ts
│ │ │ ├── ToneSwitch.tsx
│ │ │ └── ToneSwitchNative.tsx
│ │ ├── Tooltip
│ │ │ ├── Tooltip.md
│ │ │ ├── Tooltip.module.scss
│ │ │ ├── Tooltip.spec.ts
│ │ │ ├── Tooltip.tsx
│ │ │ └── TooltipNative.tsx
│ │ ├── Tree
│ │ │ ├── testData.ts
│ │ │ ├── Tree-dynamic.spec.ts
│ │ │ ├── Tree-icons.spec.ts
│ │ │ ├── Tree.md
│ │ │ ├── Tree.spec.ts
│ │ │ ├── TreeComponent.module.scss
│ │ │ ├── TreeComponent.tsx
│ │ │ └── TreeNative.tsx
│ │ ├── TreeDisplay
│ │ │ ├── TreeDisplay.md
│ │ │ ├── TreeDisplay.module.scss
│ │ │ ├── TreeDisplay.tsx
│ │ │ └── TreeDisplayNative.tsx
│ │ ├── ValidationSummary
│ │ │ ├── ValidationSummary.module.scss
│ │ │ └── ValidationSummary.tsx
│ │ └── VisuallyHidden.tsx
│ ├── components-core
│ │ ├── abstractions
│ │ │ ├── ComponentRenderer.ts
│ │ │ ├── LoaderRenderer.ts
│ │ │ ├── standalone.ts
│ │ │ └── treeAbstractions.ts
│ │ ├── action
│ │ │ ├── actions.ts
│ │ │ ├── APICall.tsx
│ │ │ ├── FileDownloadAction.tsx
│ │ │ ├── FileUploadAction.tsx
│ │ │ ├── NavigateAction.tsx
│ │ │ └── TimedAction.tsx
│ │ ├── ApiBoundComponent.tsx
│ │ ├── appContext
│ │ │ ├── date-functions.ts
│ │ │ ├── math-function.ts
│ │ │ └── misc-utils.ts
│ │ ├── AppContext.tsx
│ │ ├── behaviors
│ │ │ ├── Behavior.tsx
│ │ │ └── CoreBehaviors.tsx
│ │ ├── component-hooks.ts
│ │ ├── ComponentDecorator.tsx
│ │ ├── ComponentViewer.tsx
│ │ ├── CompoundComponent.tsx
│ │ ├── constants.ts
│ │ ├── DebugViewProvider.tsx
│ │ ├── descriptorHelper.ts
│ │ ├── devtools
│ │ │ ├── InspectorDialog.module.scss
│ │ │ ├── InspectorDialog.tsx
│ │ │ └── InspectorDialogVisibilityContext.tsx
│ │ ├── EngineError.ts
│ │ ├── event-handlers.ts
│ │ ├── InspectorButton.module.scss
│ │ ├── InspectorContext.tsx
│ │ ├── interception
│ │ │ ├── abstractions.ts
│ │ │ ├── ApiInterceptor.ts
│ │ │ ├── ApiInterceptorProvider.tsx
│ │ │ ├── apiInterceptorWorker.ts
│ │ │ ├── Backend.ts
│ │ │ ├── Errors.ts
│ │ │ ├── IndexedDb.ts
│ │ │ ├── initMock.ts
│ │ │ ├── InMemoryDb.ts
│ │ │ ├── ReadonlyCollection.ts
│ │ │ └── useApiInterceptorContext.tsx
│ │ ├── loader
│ │ │ ├── ApiLoader.tsx
│ │ │ ├── DataLoader.tsx
│ │ │ ├── ExternalDataLoader.tsx
│ │ │ ├── Loader.tsx
│ │ │ ├── MockLoaderRenderer.tsx
│ │ │ └── PageableLoader.tsx
│ │ ├── LoaderComponent.tsx
│ │ ├── markup-check.ts
│ │ ├── parts.ts
│ │ ├── renderers.ts
│ │ ├── rendering
│ │ │ ├── AppContent.tsx
│ │ │ ├── AppRoot.tsx
│ │ │ ├── AppWrapper.tsx
│ │ │ ├── buildProxy.ts
│ │ │ ├── collectFnVarDeps.ts
│ │ │ ├── ComponentAdapter.tsx
│ │ │ ├── ComponentWrapper.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── containers.ts
│ │ │ ├── ContainerWrapper.tsx
│ │ │ ├── ErrorBoundary.module.scss
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InvalidComponent.module.scss
│ │ │ ├── InvalidComponent.tsx
│ │ │ ├── nodeUtils.ts
│ │ │ ├── reducer.ts
│ │ │ ├── renderChild.tsx
│ │ │ ├── StandaloneComponent.tsx
│ │ │ ├── StateContainer.tsx
│ │ │ ├── UnknownComponent.module.scss
│ │ │ ├── UnknownComponent.tsx
│ │ │ └── valueExtractor.ts
│ │ ├── reportEngineError.ts
│ │ ├── RestApiProxy.ts
│ │ ├── script-runner
│ │ │ ├── asyncProxy.ts
│ │ │ ├── AttributeValueParser.ts
│ │ │ ├── bannedFunctions.ts
│ │ │ ├── BindingTreeEvaluationContext.ts
│ │ │ ├── eval-tree-async.ts
│ │ │ ├── eval-tree-common.ts
│ │ │ ├── eval-tree-sync.ts
│ │ │ ├── ParameterParser.ts
│ │ │ ├── process-statement-async.ts
│ │ │ ├── process-statement-common.ts
│ │ │ ├── process-statement-sync.ts
│ │ │ ├── ScriptingSourceTree.ts
│ │ │ ├── simplify-expression.ts
│ │ │ ├── statement-queue.ts
│ │ │ └── visitors.ts
│ │ ├── StandaloneApp.tsx
│ │ ├── StandaloneExtensionManager.ts
│ │ ├── TableOfContentsContext.tsx
│ │ ├── theming
│ │ │ ├── _themes.scss
│ │ │ ├── component-layout-resolver.ts
│ │ │ ├── extendThemeUtils.ts
│ │ │ ├── hvar.ts
│ │ │ ├── layout-resolver.ts
│ │ │ ├── parse-layout-props.ts
│ │ │ ├── StyleContext.tsx
│ │ │ ├── StyleRegistry.ts
│ │ │ ├── ThemeContext.tsx
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── themes
│ │ │ │ ├── base-utils.ts
│ │ │ │ ├── palette.ts
│ │ │ │ ├── root.ts
│ │ │ │ ├── solid.ts
│ │ │ │ ├── theme-colors.ts
│ │ │ │ └── xmlui.ts
│ │ │ ├── themeVars.module.scss
│ │ │ ├── themeVars.ts
│ │ │ ├── transformThemeVars.ts
│ │ │ └── utils.ts
│ │ ├── utils
│ │ │ ├── actionUtils.ts
│ │ │ ├── audio-utils.ts
│ │ │ ├── base64-utils.ts
│ │ │ ├── compound-utils.ts
│ │ │ ├── css-utils.ts
│ │ │ ├── DataLoaderQueryKeyGenerator.ts
│ │ │ ├── date-utils.ts
│ │ │ ├── extractParam.ts
│ │ │ ├── hooks.tsx
│ │ │ ├── LruCache.ts
│ │ │ ├── mergeProps.ts
│ │ │ ├── misc.ts
│ │ │ ├── request-params.ts
│ │ │ ├── statementUtils.ts
│ │ │ └── treeUtils.ts
│ │ └── xmlui-parser.ts
│ ├── index-standalone.ts
│ ├── index.scss
│ ├── index.ts
│ ├── language-server
│ │ ├── server-common.ts
│ │ ├── server-web-worker.ts
│ │ ├── server.ts
│ │ ├── services
│ │ │ ├── common
│ │ │ │ ├── docs-generation.ts
│ │ │ │ ├── lsp-utils.ts
│ │ │ │ ├── metadata-utils.ts
│ │ │ │ └── syntax-node-utilities.ts
│ │ │ ├── completion.ts
│ │ │ ├── diagnostic.ts
│ │ │ ├── format.ts
│ │ │ └── hover.ts
│ │ └── xmlui-metadata-generated.mjs
│ ├── logging
│ │ ├── LoggerContext.tsx
│ │ ├── LoggerInitializer.tsx
│ │ ├── LoggerService.ts
│ │ └── xmlui.ts
│ ├── logo.svg
│ ├── parsers
│ │ ├── common
│ │ │ ├── GenericToken.ts
│ │ │ ├── InputStream.ts
│ │ │ └── utils.ts
│ │ ├── scripting
│ │ │ ├── code-behind-collect.ts
│ │ │ ├── Lexer.ts
│ │ │ ├── modules.ts
│ │ │ ├── Parser.ts
│ │ │ ├── ParserError.ts
│ │ │ ├── ScriptingNodeTypes.ts
│ │ │ ├── TokenTrait.ts
│ │ │ ├── TokenType.ts
│ │ │ └── tree-visitor.ts
│ │ ├── style-parser
│ │ │ ├── errors.ts
│ │ │ ├── source-tree.ts
│ │ │ ├── StyleInputStream.ts
│ │ │ ├── StyleLexer.ts
│ │ │ ├── StyleParser.ts
│ │ │ └── tokens.ts
│ │ └── xmlui-parser
│ │ ├── CharacterCodes.ts
│ │ ├── diagnostics.ts
│ │ ├── fileExtensions.ts
│ │ ├── index.ts
│ │ ├── lint.ts
│ │ ├── parser.ts
│ │ ├── ParserError.ts
│ │ ├── scanner.ts
│ │ ├── syntax-kind.ts
│ │ ├── syntax-node.ts
│ │ ├── transform.ts
│ │ ├── utils.ts
│ │ ├── xmlui-serializer.ts
│ │ └── xmlui-tree.ts
│ ├── react-app-env.d.ts
│ ├── syntax
│ │ ├── monaco
│ │ │ ├── grammar.monacoLanguage.ts
│ │ │ ├── index.ts
│ │ │ ├── xmlui-dark.ts
│ │ │ ├── xmlui-light.ts
│ │ │ └── xmluiscript.monacoLanguage.ts
│ │ └── textMate
│ │ ├── index.ts
│ │ ├── xmlui-dark.json
│ │ ├── xmlui-light.json
│ │ ├── xmlui.json
│ │ └── xmlui.tmLanguage.json
│ ├── testing
│ │ ├── assertions.ts
│ │ ├── component-test-helpers.ts
│ │ ├── ComponentDrivers.ts
│ │ ├── drivers
│ │ │ ├── DateInputDriver.ts
│ │ │ ├── ModalDialogDriver.ts
│ │ │ ├── NumberBoxDriver.ts
│ │ │ ├── TextBoxDriver.ts
│ │ │ ├── TimeInputDriver.ts
│ │ │ ├── TimerDriver.ts
│ │ │ └── TreeDriver.ts
│ │ ├── fixtures.ts
│ │ ├── infrastructure
│ │ │ ├── index.html
│ │ │ ├── main.tsx
│ │ │ ├── public
│ │ │ │ ├── mockServiceWorker.js
│ │ │ │ ├── resources
│ │ │ │ │ ├── bell.svg
│ │ │ │ │ ├── box.svg
│ │ │ │ │ ├── doc.svg
│ │ │ │ │ ├── eye.svg
│ │ │ │ │ ├── flower-640x480.jpg
│ │ │ │ │ ├── sun.svg
│ │ │ │ │ ├── test-image-100x100.jpg
│ │ │ │ │ └── txt.svg
│ │ │ │ └── serve.json
│ │ │ └── TestBed.tsx
│ │ └── themed-app-test-helpers.ts
│ └── vite-env.d.ts
├── tests
│ ├── components
│ │ ├── CodeBlock
│ │ │ └── hightlight-code.test.ts
│ │ ├── playground-pattern.test.ts
│ │ └── Tree
│ │ └── Tree-states.test.ts
│ ├── components-core
│ │ ├── abstractions
│ │ │ └── treeAbstractions.test.ts
│ │ ├── container
│ │ │ └── buildProxy.test.ts
│ │ ├── interception
│ │ │ ├── orderBy.test.ts
│ │ │ ├── ReadOnlyCollection.test.ts
│ │ │ └── request-param-converter.test.ts
│ │ ├── scripts-runner
│ │ │ ├── AttributeValueParser.test.ts
│ │ │ ├── eval-tree-arrow-async.test.ts
│ │ │ ├── eval-tree-arrow.test.ts
│ │ │ ├── eval-tree-func-decl-async.test.ts
│ │ │ ├── eval-tree-func-decl.test.ts
│ │ │ ├── eval-tree-pre-post.test.ts
│ │ │ ├── eval-tree-regression.test.ts
│ │ │ ├── eval-tree.test.ts
│ │ │ ├── function-proxy.test.ts
│ │ │ ├── parser-regression.test.ts
│ │ │ ├── process-event.test.ts
│ │ │ ├── process-function.test.ts
│ │ │ ├── process-implicit-context.test.ts
│ │ │ ├── process-statement-asgn.test.ts
│ │ │ ├── process-statement-destruct.test.ts
│ │ │ ├── process-statement-regs.test.ts
│ │ │ ├── process-statement-sync.test.ts
│ │ │ ├── process-statement.test.ts
│ │ │ ├── process-switch-sync.test.ts
│ │ │ ├── process-switch.test.ts
│ │ │ ├── process-try-sync.test.ts
│ │ │ ├── process-try.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── test-metadata-handler.ts
│ │ ├── theming
│ │ │ ├── border-segments.test.ts
│ │ │ ├── component-layout.resolver.test.ts
│ │ │ ├── layout-property-parser.test.ts
│ │ │ ├── layout-resolver.test.ts
│ │ │ ├── layout-resolver2.test.ts
│ │ │ ├── layout-vp-override.test.ts
│ │ │ └── padding-segments.test.ts
│ │ └── utils
│ │ ├── date-utils.test.ts
│ │ ├── format-human-elapsed-time.test.ts
│ │ └── LruCache.test.ts
│ ├── language-server
│ │ ├── completion.test.ts
│ │ ├── format.test.ts
│ │ ├── hover.test.ts
│ │ └── mockData.ts
│ └── parsers
│ ├── common
│ │ └── input-stream.test.ts
│ ├── markdown
│ │ └── parse-binding-expression.test.ts
│ ├── parameter-parser.test.ts
│ ├── paremeter-parser.test.ts
│ ├── scripting
│ │ ├── eval-tree-arrow.test.ts
│ │ ├── eval-tree-pre-post.test.ts
│ │ ├── eval-tree.test.ts
│ │ ├── function-proxy.test.ts
│ │ ├── lexer-literals.test.ts
│ │ ├── lexer-misc.test.ts
│ │ ├── module-parse.test.ts
│ │ ├── parser-arrow.test.ts
│ │ ├── parser-assignments.test.ts
│ │ ├── parser-binary.test.ts
│ │ ├── parser-destructuring.test.ts
│ │ ├── parser-errors.test.ts
│ │ ├── parser-expressions.test.ts
│ │ ├── parser-function.test.ts
│ │ ├── parser-literals.test.ts
│ │ ├── parser-primary.test.ts
│ │ ├── parser-regex.test.ts
│ │ ├── parser-statements.test.ts
│ │ ├── parser-unary.test.ts
│ │ ├── process-event.test.ts
│ │ ├── process-implicit-context.test.ts
│ │ ├── process-statement-asgn.test.ts
│ │ ├── process-statement-destruct.test.ts
│ │ ├── process-statement-regs.test.ts
│ │ ├── process-statement-sync.test.ts
│ │ ├── process-statement.test.ts
│ │ ├── process-switch-sync.test.ts
│ │ ├── process-switch.test.ts
│ │ ├── process-try-sync.test.ts
│ │ ├── process-try.test.ts
│ │ ├── simplify-expression.test.ts
│ │ ├── statement-hooks.test.ts
│ │ └── test-helpers.ts
│ ├── style-parser
│ │ ├── generateHvarChain.test.ts
│ │ ├── parseHVar.test.ts
│ │ ├── parser.test.ts
│ │ └── tokens.test.ts
│ └── xmlui
│ ├── lint.test.ts
│ ├── parser.test.ts
│ ├── scanner.test.ts
│ ├── transform.attr.test.ts
│ ├── transform.circular.test.ts
│ ├── transform.element.test.ts
│ ├── transform.errors.test.ts
│ ├── transform.escape.test.ts
│ ├── transform.regression.test.ts
│ ├── transform.script.test.ts
│ ├── transform.test.ts
│ └── xmlui.ts
├── tests-e2e
│ ├── api-bound-component-regression.spec.ts
│ ├── api-call-as-extracted-component.spec.ts
│ ├── assign-to-object-or-array-regression.spec.ts
│ ├── binding-regression.spec.ts
│ ├── children-as-template-context-vars.spec.ts
│ ├── compound-component.spec.ts
│ ├── context-vars-regression.spec.ts
│ ├── data-bindings.spec.ts
│ ├── datasource-and-api-usage-in-var.spec.ts
│ ├── datasource-direct-binding.spec.ts
│ ├── datasource-onLoaded-regression.spec.ts
│ ├── modify-array-item-regression.spec.ts
│ ├── namespaces.spec.ts
│ ├── push-to-array-regression.spec.ts
│ ├── screen-breakpoints.spec.ts
│ ├── scripting.spec.ts
│ ├── state-scope-in-pages.spec.ts
│ └── state-var-scopes.spec.ts
├── tsconfig.bin.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/xmlui/src/parsers/scripting/Parser.ts:
--------------------------------------------------------------------------------
```typescript
import {
type ArrayDestructure,
type ArrayLiteral,
type ArrowExpression,
type AssignmentExpression,
type ScripNodeBase,
type BinaryExpression,
type BlockStatement,
type BreakStatement,
type CalculatedMemberAccessExpression,
type ConditionalExpression,
type ConstStatement,
type ContinueStatement,
type Destructure,
type DoWhileStatement,
type EmptyStatement,
type Expression,
type ExpressionStatement,
type ForInStatement,
type ForOfStatement,
type ForStatement,
type ForVarBinding,
type FunctionDeclaration,
type FunctionInvocationExpression,
type Identifier,
type IfStatement,
type LetStatement,
type Literal,
type MemberAccessExpression,
type NoArgExpression,
type ObjectDestructure,
type ObjectLiteral,
type PostfixOpExpression,
type PrefixOpExpression,
type ReactiveVarDeclaration,
type ReturnStatement,
type SequenceExpression,
type SpreadExpression,
type Statement,
type SwitchCase,
type SwitchStatement,
type ThrowStatement,
type TryStatement,
type UnaryExpression,
type VarDeclaration,
type VarStatement,
type WhileStatement,
type TemplateLiteralExpression,
T_EMPTY_STATEMENT,
T_BREAK_STATEMENT,
T_CONTINUE_STATEMENT,
T_EXPRESSION_STATEMENT,
T_VAR_DECLARATION,
T_LET_STATEMENT,
T_CONST_STATEMENT,
T_REACTIVE_VAR_DECLARATION,
T_VAR_STATEMENT,
T_ARRAY_DESTRUCTURE,
T_BLOCK_STATEMENT,
T_IF_STATEMENT,
T_WHILE_STATEMENT,
T_OBJECT_DESTRUCTURE,
T_DO_WHILE_STATEMENT,
T_RETURN_STATEMENT,
T_FOR_STATEMENT,
T_FOR_IN_STATEMENT,
T_FOR_OF_STATEMENT,
T_IDENTIFIER,
T_THROW_STATEMENT,
T_TRY_STATEMENT,
T_SWITCH_CASE,
T_SWITCH_STATEMENT,
T_NO_ARG_EXPRESSION,
T_SEQUENCE_EXPRESSION,
T_OBJECT_LITERAL,
T_ARRAY_LITERAL,
T_SPREAD_EXPRESSION,
T_FUNCTION_DECLARATION,
T_ARROW_EXPRESSION,
T_CONDITIONAL_EXPRESSION,
T_ASSIGNMENT_EXPRESSION,
T_BINARY_EXPRESSION,
T_UNARY_EXPRESSION,
T_FUNCTION_INVOCATION_EXPRESSION,
T_MEMBER_ACCESS_EXPRESSION,
T_CALCULATED_MEMBER_ACCESS_EXPRESSION,
T_POSTFIX_OP_EXPRESSION,
T_PREFIX_OP_EXPRESSION,
T_LITERAL,
T_TEMPLATE_LITERAL_EXPRESSION,
T_DESTRUCTURE,
} from "../../components-core/script-runner/ScriptingSourceTree";
import type { GenericToken } from "../common/GenericToken";
import { InputStream } from "../common/InputStream";
import { Lexer } from "./Lexer";
import { ParserError, errorMessages } from "./ParserError";
import { tokenTraits } from "./TokenTrait";
import { TokenType } from "./TokenType";
import type { ErrorCodes, ParserErrorMessage } from "./ParserError";
type Token = GenericToken<TokenType>;
/**
* States of the string parsing
*/
enum StrParseState {
Normal,
Backslash,
X,
Xh,
UX1,
UX2,
UX3,
UX4,
Ucp1,
Ucp2,
Ucp3,
Ucp4,
Ucp5,
Ucp6,
UcpTail,
}
let lastNodeId = 0;
export function createXmlUiTreeNodeId(): number {
return ++lastNodeId;
}
/**
* This class parses a binding expression and transforms it into an evaluable expression tree
*/
export class Parser {
// --- Keep track of error messages
private _parseErrors: ParserErrorMessage[] = [];
// --- Use this lexer
private _lexer: Lexer;
// --- Indicates the parsing level
private _statementLevel = 0;
/**
* Initializes the parser with the specified source code
* @param source Source code to parse
*/
constructor(source?: string) {
this._lexer = new Lexer(new InputStream(source ?? ""));
}
/**
* Sets the source code to parse
* @param source Source code to parse
*/
setSource(source: string): void {
this._lexer = new Lexer(new InputStream(source));
}
/**
* The errors raised during the parse phase
*/
get errors(): ParserErrorMessage[] {
return this._parseErrors;
}
/**
* Gets the current token
*/
get current(): Token {
return this._lexer.peek();
}
/**
* Checks if we're at the end of the expression
*/
get isEof(): boolean {
return this._lexer.peek().type === TokenType.Eof;
}
/**
* Gets the characters remaining after parsing
*/
getTail(): string {
return this._lexer.getTail();
}
// ==========================================================================
// Statement parsing
/**
* Parses a list of statements:
*
* statements
* : statement*
* ;
*
* statement
* : emptyStatement
* | expressionStatement
* | letStatement
* | returnStatement
* ;
*/
parseStatements(): Statement[] | null {
this._statementLevel = 0;
const statements: Statement[] = [];
while (!this.isEof) {
const statement = this.parseStatement();
if (!statement) return null;
statements.push(statement);
if (statement.type !== T_EMPTY_STATEMENT) {
this.skipToken(TokenType.Semicolon);
}
}
return statements;
}
/**
* Parses a single statement
*/
private parseStatement(allowSequence = true): Statement | null {
this._statementLevel++;
try {
const startToken = this._lexer.peek();
switch (startToken.type) {
case TokenType.Semicolon:
return this.parseEmptyStatement();
case TokenType.Let:
return this.parseLetStatement();
case TokenType.Const:
return this.parseConstStatement();
case TokenType.Var:
return this.parseVarStatement();
case TokenType.LBrace:
return this.parseBlockStatement();
case TokenType.If:
return this.parseIfStatement();
case TokenType.Do:
return this.parseDoWhileStatement();
case TokenType.While:
return this.parseWhileStatement();
case TokenType.Return:
return this.parseReturnStatement();
case TokenType.Break:
this._lexer.get();
return this.createStatementNode<BreakStatement>(
T_BREAK_STATEMENT,
{},
startToken,
startToken,
);
case TokenType.Continue:
this._lexer.get();
return this.createStatementNode<ContinueStatement>(
T_CONTINUE_STATEMENT,
{},
startToken,
startToken,
);
case TokenType.For:
return this.parseForStatement();
case TokenType.Throw:
return this.parseThrowStatement();
case TokenType.Try:
return this.parseTryStatement();
case TokenType.Switch:
return this.parseSwitchStatement();
case TokenType.Function:
return this.parseFunctionDeclaration();
default:
if (this.isExpressionStart(startToken)) {
return this.parseExpressionStatement(allowSequence);
}
this.reportError("W002", startToken, startToken.text);
return null;
}
} finally {
this._statementLevel--;
}
}
/**
* Parses an empty statement
*
* emptyStatement
* : ";"
* ;
*/
private parseEmptyStatement(): EmptyStatement | null {
const startToken = this._lexer.get();
return this.createStatementNode<EmptyStatement>(T_EMPTY_STATEMENT, {}, startToken, startToken);
}
/**
* Parses an expression statement
*
* expressionStatement
* : expression
* ;
*/
private parseExpressionStatement(allowSequence = true): ExpressionStatement | null {
const startToken = this._lexer.peek();
const expr = this.getExpression(allowSequence);
return expr
? this.createStatementNode<ExpressionStatement>(
T_EXPRESSION_STATEMENT,
{
expr,
},
startToken,
expr.endToken,
)
: null;
}
/**
* Parses a let statement
*
* letStatement
* : "let" id ["=" expression] ("," id ["=" expression])*
* ;
*/
private parseLetStatement(): LetStatement | null {
const startToken = this._lexer.get();
let endToken: Token | undefined = startToken;
const decls: VarDeclaration[] = [];
while (true) {
const declStart = this._lexer.peek();
let declarationProps: any = {};
if (declStart.type === TokenType.LBrace) {
// --- This is object destructure
endToken = this._lexer.ahead(1);
const oDestr = this.parseObjectDestructure();
if (oDestr === null) return null;
declarationProps = {
oDestr,
};
endToken = oDestr.length > 0 ? oDestr[oDestr.length - 1].endToken : endToken;
} else if (declStart.type === TokenType.LSquare) {
// --- This is array destructure
endToken = this._lexer.ahead(1);
const aDestr = this.parseArrayDestructure();
if (aDestr === null) return null;
declarationProps = {
aDestr,
};
endToken = aDestr.length > 0 ? aDestr[aDestr.length - 1].endToken : endToken;
} else if (declStart.type === TokenType.Identifier) {
if (declStart.text.startsWith("$")) {
this.reportError("W031");
return null;
}
endToken = this._lexer.get();
declarationProps = {
id: declStart.text,
};
} else {
this.reportError("W003");
return null;
}
// --- Optional initialization
const initToken = this._lexer.peek();
let expr: Expression | null = null;
if (initToken.type === TokenType.Assignment) {
this._lexer.get();
expr = this.getExpression(false);
if (expr === null) return null;
declarationProps.expr = expr;
endToken = expr.endToken;
} else if (declarationProps.aDestr || declarationProps.oDestr) {
this.reportError("W009", initToken);
return null;
}
// --- New declaration reached
decls.push(
this.createExpressionNode<VarDeclaration>(
T_VAR_DECLARATION,
declarationProps,
declStart,
endToken,
),
);
// --- Check for more declarations
if (this._lexer.peek().type !== TokenType.Comma) break;
this._lexer.get();
}
// --- Done
return this.createStatementNode<LetStatement>(
T_LET_STATEMENT,
{
decls,
},
startToken,
endToken,
);
}
/**
* Parses a const statement
*
* constStatement
* : "const" id "=" expression
* ;
*/
private parseConstStatement(): ConstStatement | null {
const startToken = this._lexer.get();
let endToken: Token | undefined = startToken;
const decls: VarDeclaration[] = [];
while (true) {
const declStart = this._lexer.peek();
let declarationProps: any = {};
if (declStart.type === TokenType.LBrace) {
// --- This is object destructure
endToken = this._lexer.ahead(1);
const oDestr = this.parseObjectDestructure();
if (oDestr === null) return null;
declarationProps = {
oDestr,
};
endToken = oDestr.length > 0 ? oDestr[oDestr.length - 1].endToken : endToken;
} else if (declStart.type === TokenType.LSquare) {
// --- This is array destructure
endToken = this._lexer.ahead(1);
const aDestr = this.parseArrayDestructure();
if (aDestr === null) return null;
declarationProps = {
aDestr,
};
endToken = aDestr.length > 0 ? aDestr[aDestr.length - 1].endToken : endToken;
} else if (declStart.type === TokenType.Identifier) {
if (declStart.text.startsWith("$")) {
this.reportError("W031");
return null;
}
endToken = this._lexer.get();
declarationProps = {
id: declStart.text,
};
} else {
this.reportError("W003");
return null;
}
this.expectToken(TokenType.Assignment);
const expr = this.getExpression(false);
if (expr === null) return null;
declarationProps.expr = expr;
endToken = expr.endToken;
// --- New declaration reached
decls.push(
this.createExpressionNode<VarDeclaration>(
T_VAR_DECLARATION,
declarationProps,
declStart,
endToken,
),
);
// --- Check for more declarations
if (this._lexer.peek().type !== TokenType.Comma) break;
this._lexer.get();
}
// --- Done
return this.createStatementNode<ConstStatement>(
T_CONST_STATEMENT,
{
decls,
},
startToken,
endToken,
);
}
/**
* Parses a var statement
*
* constStatement
* : "var" id "=" expression
* ;
*/
private parseVarStatement(): VarStatement | null {
const startToken = this._lexer.get();
let endToken: Token | undefined = startToken;
const decls: ReactiveVarDeclaration[] = [];
while (true) {
const declStart = this._lexer.peek();
let declarationProps: any = {};
if (declStart.type === TokenType.Identifier) {
if (declStart.text.startsWith("$")) {
this.reportError("W031");
return null;
}
endToken = this._lexer.get();
declarationProps = {
id: {
type: T_IDENTIFIER,
name: declStart.text,
startToken: declStart,
endToken,
},
};
} else {
this.reportError("W003");
return null;
}
// --- Mandatory initialization
this.expectToken(TokenType.Assignment);
const expr = this.getExpression(false);
if (expr === null) return null;
declarationProps.expr = expr;
endToken = expr.endToken;
// --- New declaration reached
decls.push(
this.createExpressionNode<ReactiveVarDeclaration>(
T_REACTIVE_VAR_DECLARATION,
declarationProps,
declStart,
endToken,
),
);
// --- Check for more declarations
if (this._lexer.peek().type !== TokenType.Comma) break;
this._lexer.get();
}
// --- Done
return this.createStatementNode<VarStatement>(
T_VAR_STATEMENT,
{
decls,
},
startToken,
endToken,
);
}
/**
* Parses an object destructure expression
*/
private parseObjectDestructure(): ObjectDestructure[] | null {
const result: ObjectDestructure[] = [];
// --- Skip the starting left brace
const startToken = this._lexer.get();
let endToken: Token | undefined = startToken;
let nextToken = this._lexer.peek();
while (nextToken.type === TokenType.Identifier) {
// --- Obtain the ID
const id = nextToken.text;
if (id.startsWith("$")) {
this.reportError("W031");
return null;
}
let alias: string | undefined;
let aDestr: ArrayDestructure[] | undefined | null;
let oDestr: ObjectDestructure[] | undefined | null;
this._lexer.get();
nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Colon) {
// --- Check the available cases
this._lexer.get();
nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Identifier) {
alias = nextToken.text;
endToken = nextToken;
this._lexer.get();
} else if (nextToken.type === TokenType.LSquare) {
aDestr = this.parseArrayDestructure();
if (aDestr === null) return null;
endToken = aDestr[aDestr.length - 1].endToken;
} else if (nextToken.type === TokenType.LBrace) {
oDestr = this.parseObjectDestructure();
if (oDestr === null) return null;
endToken = oDestr[oDestr.length - 1].endToken;
}
}
// --- Check for next segment
nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Comma || nextToken.type === TokenType.RBrace) {
result.push(
this.createExpressionNode<ObjectDestructure>(
T_OBJECT_DESTRUCTURE,
{ id, alias, aDestr, oDestr },
startToken,
endToken,
),
);
if (nextToken.type === TokenType.Comma) {
// --- Skip the delimiter comma
this._lexer.get();
nextToken = this._lexer.peek();
}
}
}
// --- Expect a closing right brace
this.expectToken(TokenType.RBrace, "W004");
return result;
}
private parseArrayDestructure(): ArrayDestructure[] | null {
const result: ArrayDestructure[] = [];
// --- Skip the starting left square
const startToken = this._lexer.get();
let endToken: Token | undefined = startToken;
do {
let nextToken = this._lexer.peek();
let id: string | undefined;
let aDestr: ArrayDestructure[] | undefined | null;
let oDestr: ObjectDestructure[] | undefined | null;
if (nextToken.type === TokenType.Identifier) {
id = nextToken.text;
if (id.startsWith("$")) {
this.reportError("W031");
return null;
}
endToken = nextToken;
nextToken = this._lexer.get();
} else if (nextToken.type === TokenType.LSquare) {
aDestr = this.parseArrayDestructure();
if (aDestr === null) return null;
endToken = aDestr[aDestr.length - 1].endToken;
} else if (nextToken.type === TokenType.LBrace) {
oDestr = this.parseObjectDestructure();
if (oDestr === null) return null;
endToken = oDestr[oDestr.length - 1].endToken;
}
nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Comma) {
// --- Store the segment
result.push(
this.createExpressionNode<ArrayDestructure>(
T_ARRAY_DESTRUCTURE,
{ id, aDestr, oDestr },
startToken,
endToken,
),
);
this._lexer.get();
} else if (nextToken.type === TokenType.RSquare) {
if (id || aDestr || oDestr) {
// --- Store a non-empty the segment
result.push(
this.createExpressionNode<ArrayDestructure>(
T_ARRAY_DESTRUCTURE,
{ id, aDestr, oDestr },
startToken,
endToken,
),
);
}
break;
} else {
this.reportError("W002", nextToken);
return null;
}
} while (true);
// --- Expect a closing right square
this.expectToken(TokenType.RSquare, "W005");
return result;
}
/**
* Parses a block statement
*
* blockStatement
* : "{" (statement [";"])* "}"
* ;
*/
private parseBlockStatement(): BlockStatement | null {
const startToken = this._lexer.get();
const stmts: Statement[] = [];
while (this._lexer.peek().type !== TokenType.RBrace) {
const statement = this.parseStatement();
if (!statement) return null;
stmts.push(statement);
if (statement.type !== T_EMPTY_STATEMENT) {
this.skipToken(TokenType.Semicolon);
}
}
const endToken = this._lexer.get();
return this.createStatementNode<BlockStatement>(
T_BLOCK_STATEMENT,
{ stmts },
startToken,
endToken,
);
}
/**
* Parses an if statement
*
* ifStatement
* : "if" "(" expression ")" statement ["else" statement]
* ;
*/
private parseIfStatement(): IfStatement | null {
const startToken = this._lexer.get();
let endToken: Token | undefined = startToken;
this.expectToken(TokenType.LParent, "W014");
const cond = this.getExpression();
if (!cond) return null;
this.expectToken(TokenType.RParent, "W006");
const thenB = this.parseStatement();
if (!thenB) return null;
endToken = thenB.endToken;
let elseCanFollow = true;
if (thenB.type !== T_BLOCK_STATEMENT) {
// --- We need a closing semicolon before else
if (this._lexer.peek().type === TokenType.Semicolon) {
this._lexer.get();
} else {
elseCanFollow = false;
}
}
let elseB: Statement | null = null;
if (elseCanFollow && this._lexer.peek().type === TokenType.Else) {
this._lexer.get();
elseB = this.parseStatement();
if (!elseB) return null;
endToken = elseB.endToken;
}
return this.createStatementNode<IfStatement>(
T_IF_STATEMENT,
{
cond,
thenB,
elseB,
},
startToken,
endToken,
);
}
/**
* Parses a while statement
*
* whileStatement
* : "while" "(" condition ")" statement
* ;
*/
private parseWhileStatement(): WhileStatement | null {
const startToken = this._lexer.get();
this.expectToken(TokenType.LParent, "W014");
const cond = this.getExpression();
if (!cond) return null;
this.expectToken(TokenType.RParent, "W006");
const body = this.parseStatement();
if (!body) return null;
return this.createStatementNode<WhileStatement>(
T_WHILE_STATEMENT,
{
cond,
body,
},
startToken,
body.endToken,
);
}
/**
* Parses a do-while statement
*
* doWhileStatement
* : "do" statement "while" "(" condition ")"
* ;
*/
private parseDoWhileStatement(): DoWhileStatement | null {
const startToken = this._lexer.get();
const body = this.parseStatement();
if (!body) return null;
if (body.type !== T_BLOCK_STATEMENT && body.type !== T_EMPTY_STATEMENT) {
this.expectToken(TokenType.Semicolon);
}
this.expectToken(TokenType.While);
this.expectToken(TokenType.LParent, "W014");
const cond = this.getExpression();
if (!cond) return null;
const endToken = this._lexer.peek();
this.expectToken(TokenType.RParent, "W006");
return this.createStatementNode<DoWhileStatement>(
T_DO_WHILE_STATEMENT,
{
cond,
body,
},
startToken,
endToken,
);
}
/**
* Parses an expression statement
*
* returnStatement
* : "return" expression?
* ;
*/
private parseReturnStatement(): ReturnStatement | null {
const startToken = this._lexer.peek();
let endToken: Token | undefined = this._lexer.get();
let expr: Expression | undefined | null;
if (tokenTraits[this._lexer.peek().type].expressionStart) {
expr = this.getExpression();
if (expr === null) return null;
endToken = expr.endToken;
}
return this.createStatementNode<ReturnStatement>(
T_RETURN_STATEMENT,
{
expr,
},
startToken,
endToken,
);
}
/**
* forStatement
* : "for" "(" initStatement? ";" expression? ";" expression? ")" statement
* | forInOfStatement
* ;
*/
private parseForStatement(): ForStatement | ForInStatement | ForOfStatement | null {
const startToken = this._lexer.peek();
this._lexer.get();
this.expectToken(TokenType.LParent, "W014");
// --- Check for..in and for..of
let nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Identifier) {
if (this._lexer.ahead(1).type === TokenType.In) {
return this.parseForInOfStatement(startToken, "none", nextToken, T_FOR_IN_STATEMENT);
} else if (this._lexer.ahead(1).type === TokenType.Of) {
return this.parseForInOfStatement(startToken, "none", nextToken, T_FOR_OF_STATEMENT);
}
} else if (nextToken.type === TokenType.Let) {
const idToken = this._lexer.ahead(1);
if (idToken.type === TokenType.Identifier) {
const inOfToken = this._lexer.ahead(2);
if (inOfToken.type === TokenType.In) {
return this.parseForInOfStatement(startToken, "let", idToken, T_FOR_IN_STATEMENT);
} else if (inOfToken.type === TokenType.Of) {
return this.parseForInOfStatement(startToken, "let", idToken, T_FOR_OF_STATEMENT);
}
}
} else if (nextToken.type === TokenType.Const) {
const idToken = this._lexer.ahead(1);
if (idToken.type === TokenType.Identifier) {
const inOfToken = this._lexer.ahead(2);
if (inOfToken.type === TokenType.In) {
return this.parseForInOfStatement(startToken, "const", idToken, T_FOR_IN_STATEMENT);
} else if (inOfToken.type === TokenType.Of) {
return this.parseForInOfStatement(startToken, "const", idToken, T_FOR_OF_STATEMENT);
}
}
}
// --- We accept only let statement, empty statement, and expression
let init: ExpressionStatement | LetStatement | undefined;
nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Semicolon) {
// --- Empty statement: no init in the for loop
this._lexer.get();
} else if (nextToken.type === TokenType.Let) {
// --- Let statement
const letStmt = this.parseLetStatement();
if (letStmt === null) {
return null;
}
init = letStmt;
if (init.decls.some((d) => !d.expr)) {
this.reportError("W011");
return null;
}
this.expectToken(TokenType.Semicolon);
} else if (tokenTraits[nextToken.type].expressionStart) {
// --- Expression statement
const exprStmt = this.parseExpressionStatement();
if (exprStmt === null) {
return null;
}
init = exprStmt;
this.expectToken(TokenType.Semicolon);
}
// --- Parse the condition
let cond: Expression | null | undefined;
nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Semicolon) {
// --- No condition
this._lexer.get();
} else {
cond = this.getExpression();
if (cond === null) {
return null;
}
this.expectToken(TokenType.Semicolon);
}
// --- Parse the update expression
let upd: Expression | null | undefined;
nextToken = this._lexer.peek();
if (nextToken.type !== TokenType.RParent) {
upd = this.getExpression();
if (upd === null) {
return null;
}
}
// --- Close the declaration part
this.expectToken(TokenType.RParent, "W006");
// --- Get the body
const body = this.parseStatement();
if (!body) return null;
return this.createStatementNode<ForStatement>(
T_FOR_STATEMENT,
{
init,
cond,
upd,
body,
},
startToken,
body.endToken,
);
}
/**
* forInOfStatement
* : "for" "(" [ "let" | "const" ] identifier ( "in" | "of" ) expression? ")" statement
* | forInOfStatement
* ;
*
* @param startToken Statement start token
* @param varB Variable binding of the for..in/of statement
* @param id ID name
* @param type Is it a for..in or a for..of?
*/
private parseForInOfStatement(
startToken: Token,
varB: ForVarBinding,
idToken: Token,
type: number,
): ForInStatement | ForOfStatement | null {
if (varB !== "none") {
// --- Skip variable binding type
if (idToken.text.startsWith("$")) {
this.reportError("W031");
return null;
}
this._lexer.get();
}
// --- Skip variable name, in/of token
this._lexer.get();
this._lexer.get();
// --- Get the expression
const expr = this.getExpression(true);
// --- Close the declaration part
this.expectToken(TokenType.RParent, "W006");
// --- Get the body
const body = this.parseStatement();
if (!body) return null;
return type === T_FOR_IN_STATEMENT
? this.createStatementNode<ForInStatement>(
T_FOR_IN_STATEMENT,
{
varB,
id: {
type: T_IDENTIFIER,
name: idToken.text,
startToken: idToken,
endToken: idToken,
},
expr,
body,
},
startToken,
body.endToken,
)
: this.createStatementNode<ForOfStatement>(
T_FOR_OF_STATEMENT,
{
varB,
id: {
type: T_IDENTIFIER,
name: idToken.text,
startToken: idToken,
endToken: idToken,
},
expr,
body,
},
startToken,
body.endToken,
);
}
/**
* Parses a throw statement
*
* throwStatement
* : "throw" expression
* ;
*/
private parseThrowStatement(): ThrowStatement | null {
const startToken = this._lexer.peek();
this._lexer.get();
let expr: Expression | undefined | null;
expr = this.getExpression();
if (expr === null) return null;
return this.createStatementNode<ThrowStatement>(
T_THROW_STATEMENT,
{
expr,
},
startToken,
expr.endToken,
);
}
/**
* Parses a try..catch..finally statement
*
* tryStatement
* : "try" blockStatement catchClause finallyClause?
* | "try" blockStatement catchClause? finallyClause
* ;
*
* catchClause
* : "catch" [ "(" identifier ") ]? blockStatement
* ;
*
* finallyClause
* : "finally" blockStatement
*/
private parseTryStatement(): TryStatement | null {
const getBlock: () => BlockStatement | null = () => {
const nextToken = this._lexer.peek();
if (nextToken.type !== TokenType.LBrace) {
this.reportError("W012", nextToken);
return null;
}
return this.parseBlockStatement();
};
const startToken = this._lexer.peek();
let endToken: Token | undefined = this._lexer.get();
// --- Get "try" block
const tryB = getBlock()!;
let catchB: BlockStatement | undefined;
let catchV: Identifier | undefined;
let finallyB: BlockStatement | undefined;
let nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Catch) {
// --- Catch found
this._lexer.get();
nextToken = this._lexer.peek();
if (nextToken.type === TokenType.LParent) {
// --- Catch variable found
this._lexer.get();
nextToken = this._lexer.peek();
if (nextToken.type !== TokenType.Identifier) {
this.reportError("W003", nextToken);
return null;
}
catchV = {
type: T_IDENTIFIER,
nodeId: createXmlUiTreeNodeId(),
name: nextToken.text,
startToken: nextToken,
endToken: nextToken,
};
this._lexer.get();
this.expectToken(TokenType.RParent, "W006");
}
// --- Get catch block
catchB = getBlock()!;
endToken = catchB.endToken;
if (this._lexer.peek().type === TokenType.Finally) {
// --- Finally after catch
this._lexer.get();
finallyB = getBlock()!;
endToken = finallyB.endToken;
}
} else if (nextToken.type === TokenType.Finally) {
// --- Finally found
this._lexer.get();
finallyB = getBlock()!;
endToken = finallyB.endToken;
} else {
this.reportError("W013", nextToken);
return null;
}
return this.createStatementNode<TryStatement>(
T_TRY_STATEMENT,
{
tryB,
catchB,
catchV,
finallyB,
},
startToken,
endToken,
);
}
/**
* Parses a switch statement
*
* switchStatement
* : "switch" "(" expression ")" "{" caseClauses "}"
* ;
*
* caseClauses
* : "case" expression ":" statement*
* | "default" ":" statement*
* ;
*/
private parseSwitchStatement(): SwitchStatement | null {
const startToken = this._lexer.get();
// --- Parse the switch expression
this.expectToken(TokenType.LParent, "W014");
const expr = this.getExpression();
if (!expr) return null;
this.expectToken(TokenType.RParent, "W006");
// --- Parse the case blocks
this.expectToken(TokenType.LBrace, "W012");
const cases: SwitchCase[] = [];
let defaultCaseFound = false;
while (true) {
let nextToken = this._lexer.peek();
let caseE: Expression | null | undefined;
if (nextToken.type === TokenType.Case) {
// --- Process "case"
this._lexer.get();
caseE = this.getExpression();
if (!caseE) return null;
} else if (nextToken.type === TokenType.Default) {
// --- Process "default"
if (defaultCaseFound) {
this.reportError("W016");
return null;
}
defaultCaseFound = true;
this._lexer.get();
} else if (nextToken.type === TokenType.RBrace) {
break;
} else {
this.reportError("W015");
return null;
}
// --- Process label statements
this.expectToken(TokenType.Colon, "W008");
let stmts: Statement[] = [];
let collected = false;
while (!collected) {
const stmtToken = this._lexer.peek();
switch (stmtToken.type) {
case TokenType.Case:
case TokenType.Default:
case TokenType.RBrace:
// --- No more case to process
collected = true;
break;
default:
// --- Try to get the next statement
const stmt = this.parseStatement();
if (stmt === null) {
collected = true;
break;
}
stmts.push(stmt);
if (stmt.type !== T_EMPTY_STATEMENT) {
this.skipToken(TokenType.Semicolon);
}
}
}
cases.push(
this.createNode(
T_SWITCH_CASE,
{
caseE,
stmts,
},
startToken,
),
);
}
// --- Closing
const endToken = this._lexer.peek();
this.expectToken(TokenType.RBrace, "W004");
return this.createStatementNode<SwitchStatement>(
T_SWITCH_STATEMENT,
{
expr,
cases,
},
startToken,
endToken,
);
}
/**
* Parses a function declaration
*
* functionDeclaration
* : "function" identifier "(" [parameterList] ")" blockStatement
* ;
*/
private parseFunctionDeclaration(allowNoName = false): FunctionDeclaration | null {
const startToken = this._lexer.get();
// --- Get the function name
let functionName: string | undefined;
const funcId = this._lexer.peek();
if (allowNoName) {
if (funcId.type !== TokenType.LParent) {
if (funcId.type !== TokenType.Identifier) {
this.reportError("W003", funcId);
return null;
}
functionName = funcId.text;
this._lexer.get();
}
} else {
if (funcId.type !== TokenType.Identifier) {
this.reportError("W003", funcId);
return null;
}
functionName = funcId.text;
this._lexer.get();
}
// --- Get the parameter list;
const nextToken = this._lexer.peek();
if (nextToken.type !== TokenType.LParent) {
this.reportError("W014", nextToken);
return null;
}
// --- Now, get the parameter list as an expression
const exprList = this.getExpression(true);
// --- We turn the expression into a parameter list
let isValid: boolean;
const args: Expression[] = [];
switch (exprList!.type) {
case T_NO_ARG_EXPRESSION:
isValid = true;
break;
case T_IDENTIFIER:
isValid = (exprList.parenthesized ?? 0) <= 1;
args.push(exprList);
break;
case T_SEQUENCE_EXPRESSION:
isValid = exprList.parenthesized === 1;
let spreadFound = false;
if (isValid) {
for (const expr of exprList.exprs) {
// --- Spread operator can be used only in the last position
if (spreadFound) {
isValid = false;
break;
}
switch (expr.type) {
case T_IDENTIFIER:
isValid = !expr.parenthesized;
args.push(expr);
break;
case T_OBJECT_LITERAL: {
isValid = !expr.parenthesized;
if (isValid) {
const des = this.convertToObjectDestructure(expr);
if (des) args.push(des);
}
break;
}
case T_ARRAY_LITERAL: {
isValid = !expr.parenthesized;
if (isValid) {
const des = this.convertToArrayDestructure(expr);
if (des) args.push(des);
}
break;
}
case T_SPREAD_EXPRESSION: {
spreadFound = true;
if (expr.expr.type !== T_IDENTIFIER) {
isValid = false;
break;
}
args.push(expr);
break;
}
default:
isValid = false;
break;
}
}
}
break;
case T_OBJECT_LITERAL:
isValid = exprList.parenthesized === 1;
if (isValid) {
const des = this.convertToObjectDestructure(exprList);
if (des) args.push(des);
}
break;
case T_ARRAY_LITERAL:
isValid = exprList.parenthesized === 1;
if (isValid) {
const des = this.convertToArrayDestructure(exprList);
if (des) args.push(des);
}
break;
case T_SPREAD_EXPRESSION:
if (exprList.expr.type !== T_IDENTIFIER) {
isValid = false;
break;
}
isValid = true;
args.push(exprList);
break;
default:
isValid = false;
}
if (!isValid) {
this.reportError("W010", startToken);
return null;
}
// --- Get the function body (statement block)
if (this._lexer.peek().type !== TokenType.LBrace) {
this.reportError("W012", this._lexer.peek());
return null;
}
const stmt = this.parseBlockStatement();
if (!stmt) return null;
// --- Done.
return this.createStatementNode<FunctionDeclaration>(
T_FUNCTION_DECLARATION,
{
id: { type: T_IDENTIFIER, name: functionName },
args,
stmt,
},
startToken,
stmt.endToken,
);
}
// ==========================================================================
// Expression parsing
/**
* Parses an expression:
*
* expr
* : sequenceExpr
* ;
*/
parseExpr(allowSequence = true): Expression | null {
return allowSequence
? this.parseSequenceExpression()
: this.parseCondOrSpreadOrAsgnOrArrowExpr();
}
/**
* sequenceExpr
* : conditionalExpr ( "," conditionalExpr )?
*/
private parseSequenceExpression(): Expression | null {
const start = this._lexer.peek();
let endToken: Token | undefined = start;
let leftExpr = this.parseCondOrSpreadOrAsgnOrArrowExpr();
if (!leftExpr) {
return null;
}
endToken = leftExpr.endToken;
const exprs: Expression[] = [];
let loose = false;
if (this._lexer.peek().type === TokenType.Comma) {
exprs.push(leftExpr);
while (this.skipToken(TokenType.Comma)) {
if (this._lexer.peek().type === TokenType.Comma) {
loose = true;
endToken = this._lexer.peek();
exprs.push(
this.createExpressionNode<NoArgExpression>(T_NO_ARG_EXPRESSION, {}, endToken, endToken),
);
} else {
const nextExpr = this.parseCondOrSpreadOrAsgnOrArrowExpr();
if (!nextExpr) {
break;
}
endToken = nextExpr.endToken;
exprs.push(nextExpr);
}
}
}
if (!exprs.length) {
// --- No sequence
return leftExpr;
}
// --- Create the sequence expression
leftExpr = this.createExpressionNode<SequenceExpression>(
T_SEQUENCE_EXPRESSION,
{
exprs,
loose,
},
start,
endToken,
);
// --- Check for "loose" sequence expression
if (loose) {
leftExpr = this.convertToArrayDestructure(leftExpr);
}
// --- Done.
return leftExpr;
}
/**
* conditionalOrSpreadOrAsgnOrArrowExpr
* : nullCoalescingExpr ( "?" expr ":" expr )?
* | "..." nullCoalescingExpr
* | identifier "=" expr
* ;
*/
private parseCondOrSpreadOrAsgnOrArrowExpr(): Expression | null {
const startToken = this._lexer.peek();
if (startToken.type === TokenType.Spread) {
// --- Spread expression
this._lexer.get();
const spreadOperand = this.parseNullCoalescingExpr();
return spreadOperand
? this.createExpressionNode<SpreadExpression>(
T_SPREAD_EXPRESSION,
{
expr: spreadOperand,
},
startToken,
spreadOperand.endToken,
)
: null;
}
if (startToken.type === TokenType.Function) {
const funcDecl = this.parseFunctionDeclaration(true);
return funcDecl
? this.createExpressionNode<ArrowExpression>(
T_ARROW_EXPRESSION,
{
name: funcDecl.id.name,
args: funcDecl.args,
statement: funcDecl.stmt,
},
startToken,
funcDecl.endToken,
)
: null;
}
const otherExpr = this.parseNullCoalescingExpr();
if (!otherExpr) {
return null;
}
const nextToken = this._lexer.peek();
if (nextToken.type === TokenType.Arrow) {
// --- It is an arrow expression
return this.parseArrowExpression(startToken, otherExpr);
}
if (nextToken.type === TokenType.QuestionMark) {
this._lexer.get();
// --- Conditional expression
const trueExpr = this.getExpression(false);
this.expectToken(TokenType.Colon);
const falseExpr = this.getExpression(false);
return this.createExpressionNode<ConditionalExpression>(
T_CONDITIONAL_EXPRESSION,
{
cond: otherExpr,
thenE: trueExpr,
elseE: falseExpr,
},
startToken,
falseExpr!.endToken,
);
}
if (tokenTraits[nextToken.type].isAssignment) {
// --- Assignment
this._lexer.get();
const expr = this.getExpression();
return expr
? this.createExpressionNode<AssignmentExpression>(
T_ASSIGNMENT_EXPRESSION,
{
leftValue: otherExpr,
op: nextToken.text,
expr,
},
startToken,
expr.endToken,
)
: null;
}
return otherExpr;
}
/**
* Parses an arrow expression
* @param start Start token
* @param left Expression to the left from the arrow
*/
private parseArrowExpression(start: Token, left: Expression): ArrowExpression | null {
// --- Check the left expression
let isValid: boolean;
const args: Expression[] = [];
switch (left.type) {
case T_NO_ARG_EXPRESSION:
isValid = true;
break;
case T_IDENTIFIER:
isValid = (left.parenthesized ?? 0) <= 1;
args.push(left);
break;
case T_SEQUENCE_EXPRESSION:
isValid = left.parenthesized === 1;
let spreadFound = false;
if (isValid) {
for (const expr of left.exprs) {
if (spreadFound) {
isValid = false;
break;
}
switch (expr.type) {
case T_IDENTIFIER:
isValid = !expr.parenthesized;
args.push(expr);
break;
case T_OBJECT_LITERAL: {
isValid = !expr.parenthesized;
if (isValid) {
const des = this.convertToObjectDestructure(expr);
if (des) args.push(des);
}
break;
}
case T_ARRAY_LITERAL: {
isValid = !expr.parenthesized;
if (isValid) {
const des = this.convertToArrayDestructure(expr);
if (des) args.push(des);
}
break;
}
case T_SPREAD_EXPRESSION: {
spreadFound = true;
if (expr.expr.type !== T_IDENTIFIER) {
isValid = false;
break;
}
args.push(expr);
break;
}
default:
isValid = false;
break;
}
}
}
break;
case T_OBJECT_LITERAL:
isValid = left.parenthesized === 1;
if (isValid) {
const des = this.convertToObjectDestructure(left);
if (des) args.push(des);
}
break;
case T_ARRAY_LITERAL:
isValid = left.parenthesized === 1;
if (isValid) {
const des = this.convertToArrayDestructure(left);
if (des) args.push(des);
}
break;
case T_SPREAD_EXPRESSION:
isValid = left.expr.type === T_IDENTIFIER;
if (isValid) {
args.push(left);
}
break;
default:
isValid = false;
}
if (!isValid) {
this.reportError("W010", start);
return null;
}
// --- Skip the arrow token
this._lexer.get();
// --- Get arrow expression statements
const statement = this.parseStatement(false);
return statement
? this.createExpressionNode<ArrowExpression>(
T_ARROW_EXPRESSION,
{
args,
statement,
},
start,
statement.endToken,
)
: null;
}
/**
* nullCoalescingExpr
* : logicalOrExpr ( "??" logicalOrExpr )?
* ;
*/
private parseNullCoalescingExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseLogicalOrExpr();
if (!leftExpr) {
return null;
}
while (this.skipToken(TokenType.NullCoalesce)) {
const rightExpr = this.parseLogicalOrExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: "??",
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* logicalOrExpr
* : logicalAndExpr ( "||" logicalAndExpr )?
* ;
*/
private parseLogicalOrExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseLogicalAndExpr();
if (!leftExpr) {
return null;
}
while (this.skipToken(TokenType.LogicalOr)) {
const rightExpr = this.parseLogicalAndExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: "||",
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* logicalAndExpr
* : bitwiseOrExpr ( "&&" bitwiseOrExpr )?
* ;
*/
private parseLogicalAndExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseBitwiseOrExpr();
if (!leftExpr) {
return null;
}
while (this.skipToken(TokenType.LogicalAnd)) {
const rightExpr = this.parseBitwiseOrExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: "&&",
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* bitwiseOrExpr
* : bitwiseXorExpr ( "|" bitwiseXorExpr )?
* ;
*/
private parseBitwiseOrExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseBitwiseXorExpr();
if (!leftExpr) {
return null;
}
while (this.skipToken(TokenType.BitwiseOr)) {
const rightExpr = this.parseBitwiseXorExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: "|",
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* bitwiseXorExpr
* : bitwiseAndExpr ( "^" bitwiseAndExpr )?
* ;
*/
private parseBitwiseXorExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseBitwiseAndExpr();
if (!leftExpr) {
return null;
}
while (this.skipToken(TokenType.BitwiseXor)) {
const rightExpr = this.parseBitwiseAndExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: "^",
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* bitwiseAndExpr
* : equExpr ( "&" equExpr )?
* ;
*/
private parseBitwiseAndExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseEquExpr();
if (!leftExpr) {
return null;
}
while (this.skipToken(TokenType.BitwiseAnd)) {
const rightExpr = this.parseEquExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: "&",
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* equExpr
* : relOrInExpr ( ( "==" | "!=" | "===" | "!==" ) relOrInExpr )?
* ;
*/
private parseEquExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseRelOrInExpr();
if (!leftExpr) {
return null;
}
let opType: Token | null;
while (
(opType = this.skipTokens(
TokenType.Equal,
TokenType.StrictEqual,
TokenType.NotEqual,
TokenType.StrictNotEqual,
))
) {
const rightExpr = this.parseRelOrInExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: opType.text,
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* relOrInExpr
* : shiftExpr ( ( "<" | "<=" | ">" | ">=", "in" ) shiftExpr )?
* ;
*/
private parseRelOrInExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseShiftExpr();
if (!leftExpr) {
return null;
}
let opType: Token | null;
while (
(opType = this.skipTokens(
TokenType.LessThan,
TokenType.LessThanOrEqual,
TokenType.GreaterThan,
TokenType.GreaterThanOrEqual,
TokenType.In,
))
) {
const rightExpr = this.parseShiftExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: opType.text,
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* shiftExpr
* : addExpr ( ( "<<" | ">>" | ">>>" ) addExpr )?
* ;
*/
private parseShiftExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseAddExpr();
if (!leftExpr) {
return null;
}
let opType: Token | null;
while (
(opType = this.skipTokens(
TokenType.ShiftLeft,
TokenType.ShiftRight,
TokenType.SignedShiftRight,
))
) {
const rightExpr = this.parseAddExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: opType.text,
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* addExpr
* : multExpr ( ( "+" | "-" ) multExpr )?
* ;
*/
private parseAddExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseMultExpr();
if (!leftExpr) {
return null;
}
let opType: Token | null;
while ((opType = this.skipTokens(TokenType.Plus, TokenType.Minus))) {
const rightExpr = this.parseMultExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: opType.text,
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* multExpr
* : exponentialExpr ( ( "*" | "/" | "%") exponentialExpr )?
* ;
*/
private parseMultExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseExponentialExpr();
if (!leftExpr) {
return null;
}
let opType: Token | null;
while ((opType = this.skipTokens(TokenType.Multiply, TokenType.Divide, TokenType.Remainder))) {
const rightExpr = this.parseExponentialExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: opType.text,
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
}
return leftExpr;
}
/**
* exponentialExpr
* : unaryExpr ( "**" unaryExpr )?
* ;
*/
private parseExponentialExpr(): Expression | null {
const startToken = this._lexer.peek();
let leftExpr = this.parseUnaryOrPrefixExpr();
if (!leftExpr) {
return null;
}
let opType: Token | null;
let count = 0;
while ((opType = this.skipToken(TokenType.Exponent))) {
let rightExpr = this.parseUnaryOrPrefixExpr();
if (!rightExpr) {
this.reportError("W001");
return null;
}
let endToken = rightExpr.endToken;
if (count === 0) {
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: opType.text,
left: leftExpr,
right: rightExpr,
},
startToken,
endToken,
);
} else {
const prevLeft = leftExpr as BinaryExpression;
leftExpr = this.createExpressionNode<BinaryExpression>(
T_BINARY_EXPRESSION,
{
op: opType.text,
left: prevLeft.left,
right: {
type: T_BINARY_EXPRESSION,
op: opType.text,
left: prevLeft.right,
right: rightExpr,
},
},
startToken,
endToken,
);
}
count++;
}
return leftExpr;
}
/**
* unaryExpr
* : ( "typeof" | "delete" | "+" | "-" | "~" | "!" ) memberOrInvocationExpr
* | memberOrInvocationExpr
* ;
*/
private parseUnaryOrPrefixExpr(): Expression | null {
const startToken = this._lexer.peek();
if (tokenTraits[startToken.type].canBeUnary) {
this._lexer.get();
const unaryOperand = this.parseUnaryOrPrefixExpr();
if (!unaryOperand) {
return null;
}
return this.createExpressionNode<UnaryExpression>(
T_UNARY_EXPRESSION,
{
op: startToken.text,
expr: unaryOperand,
},
startToken,
unaryOperand.endToken,
);
}
if (startToken.type === TokenType.IncOp || startToken.type === TokenType.DecOp) {
this._lexer.get();
const prefixOperand = this.parseMemberOrInvocationExpr();
if (!prefixOperand) {
return null;
}
return this.createExpressionNode<PrefixOpExpression>(
T_PREFIX_OP_EXPRESSION,
{
op: startToken.text,
expr: prefixOperand,
},
startToken,
prefixOperand.endToken,
);
}
// --- Not unary or prefix
return this.parseMemberOrInvocationExpr();
}
/**
* memberOrInvocationExpr
* : primaryExpr "(" functionArgs ")"
* | primaryExpr "." identifier
* | primaryExpr "?." identifier
* | primaryExpr "[" expr "]"
* ;
*/
private parseMemberOrInvocationExpr(): Expression | null {
const startToken = this._lexer.peek();
let primary = this.parsePrimaryExpr();
if (!primary) {
return null;
}
let exitLoop = false;
do {
const currentStart = this._lexer.peek();
switch (currentStart.type) {
case TokenType.LParent: {
this._lexer.get();
let args: Expression[] = [];
if (this._lexer.peek().type !== TokenType.RParent) {
const expr = this.parseExpr();
if (!expr) {
this.reportError("W001");
return null;
}
args = expr.type === T_SEQUENCE_EXPRESSION ? expr.exprs : [expr];
}
const endToken = this._lexer.peek();
this.expectToken(TokenType.RParent, "W006");
primary = this.createExpressionNode<FunctionInvocationExpression>(
T_FUNCTION_INVOCATION_EXPRESSION,
{
obj: primary,
arguments: args,
},
startToken,
endToken,
);
break;
}
case TokenType.Dot:
case TokenType.OptionalChaining:
this._lexer.get();
const member = this._lexer.get();
const memberTrait = tokenTraits[member.type];
if (!memberTrait.keywordLike) {
this.reportError("W003");
return null;
}
primary = this.createExpressionNode<MemberAccessExpression>(
T_MEMBER_ACCESS_EXPRESSION,
{
obj: primary,
member: member.text,
opt: currentStart.type === TokenType.OptionalChaining,
},
startToken,
member,
);
break;
case TokenType.LSquare:
this._lexer.get();
const memberExpr = this.getExpression();
if (!memberExpr) {
return null;
}
const endToken = this._lexer.peek();
this.expectToken(TokenType.RSquare, "W005");
primary = this.createExpressionNode<CalculatedMemberAccessExpression>(
T_CALCULATED_MEMBER_ACCESS_EXPRESSION,
{
obj: primary,
member: memberExpr,
},
startToken,
endToken,
);
break;
default:
exitLoop = true;
break;
}
} while (!exitLoop);
// --- Check for postfix operators
const nextToken = this._lexer.peek();
if (nextToken.type === TokenType.IncOp || nextToken.type === TokenType.DecOp) {
this._lexer.get();
return this.createExpressionNode<PostfixOpExpression>(
T_POSTFIX_OP_EXPRESSION,
{
op: nextToken.text,
expr: primary,
},
startToken,
nextToken,
);
}
return primary;
}
/**
* primaryExpr
* : literal
* | identifier
* | "::" identifier
* | "$item"
* | "(" expr ")"
* ;
*/
private parsePrimaryExpr(): Expression | null {
const start = this._lexer.peek();
switch (start.type) {
case TokenType.LParent:
// --- Parenthesised or no-arg expression
this._lexer.get();
if (this._lexer.peek().type === TokenType.RParent) {
// --- No-arg
const endToken = this._lexer.get();
return this.createExpressionNode<NoArgExpression>(
T_NO_ARG_EXPRESSION,
{},
start,
endToken,
);
}
// --- Parenthesized
let parenthesizedExpr = this.parseExpr();
if (!parenthesizedExpr) {
return null;
}
const endToken = this._lexer.peek();
this.expectToken(TokenType.RParent, "W006");
return {
...parenthesizedExpr,
parenthesized: (parenthesizedExpr.parenthesized ?? 0) + 1,
startToken: start,
endToken,
};
case TokenType.Identifier: {
const idToken = this._lexer.get();
return this.createExpressionNode<Identifier>(
T_IDENTIFIER,
{
name: idToken.text,
},
idToken,
idToken,
);
}
case TokenType.Global: {
this._lexer.get();
const idToken = this._lexer.get();
if (idToken.type !== TokenType.Identifier) {
this.reportError("W003");
return null;
}
return this.createExpressionNode<Identifier>(
T_IDENTIFIER,
{
name: idToken.text,
isGlobal: true,
},
idToken,
idToken,
);
}
case TokenType.Backtick:
return this.parseTemplateLiteral();
case TokenType.False:
case TokenType.True:
this._lexer.get();
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value: start.type === TokenType.True,
},
start,
start,
);
case TokenType.BinaryLiteral:
this._lexer.get();
return this.parseBinaryLiteral(start);
case TokenType.DecimalLiteral:
this._lexer.get();
return this.parseDecimalLiteral(start);
case TokenType.HexadecimalLiteral:
this._lexer.get();
return this.parseHexadecimalLiteral(start);
case TokenType.RealLiteral:
this._lexer.get();
return this.parseRealLiteral(start);
case TokenType.StringLiteral:
this._lexer.get();
return this.parseStringLiteral(start);
case TokenType.Infinity:
this._lexer.get();
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value: Infinity,
},
start,
start,
);
case TokenType.NaN:
this._lexer.get();
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value: NaN,
},
start,
start,
);
case TokenType.Null:
this._lexer.get();
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value: null,
},
start,
start,
);
case TokenType.Undefined:
this._lexer.get();
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value: undefined,
},
start,
start,
);
case TokenType.LSquare:
return this.parseArrayLiteral();
case TokenType.LBrace:
return this.parseObjectLiteral();
case TokenType.Divide:
return this.parseRegExpLiteral();
}
return null;
}
private parseTemplateLiteral(): TemplateLiteralExpression {
const startToken = this._lexer.get();
this._lexer.setStartingPhaseToTemplateLiteral();
const segments: (Literal | Expression)[] = [];
loop: while (true) {
let nextToken = this._lexer.peek();
switch (nextToken.type) {
case TokenType.StringLiteral:
this._lexer.get();
const str = this.parseStringLiteral(nextToken, false);
segments.push(str);
break;
case TokenType.DollarLBrace:
this._lexer.get();
const innerExpr = this.parseExpr();
segments.push(innerExpr);
this.expectToken(TokenType.RBrace, "W004");
this._lexer.setStartingPhaseToTemplateLiteral();
break;
case TokenType.Backtick:
break loop;
default:
this.reportError("W004");
}
}
const endToken = this._lexer.get();
return this.createExpressionNode<TemplateLiteralExpression>(
T_TEMPLATE_LITERAL_EXPRESSION,
{ segments },
startToken,
endToken,
);
}
/**
* Parses an array literal
*/
private parseArrayLiteral(): ArrayLiteral | null {
const start = this._lexer.get();
let expressions: Expression[] = [];
if (this._lexer.peek().type !== TokenType.RSquare) {
const expr = this.getExpression();
if (expr) {
expressions = expr.type === T_SEQUENCE_EXPRESSION ? expr.exprs : [expr];
}
}
const endToken = this._lexer.peek();
this.expectToken(TokenType.RSquare);
return this.createExpressionNode<ArrayLiteral>(
T_ARRAY_LITERAL,
{
items: expressions,
},
start,
endToken,
);
}
/**
* Parses an object literal
*/
private parseObjectLiteral(): ObjectLiteral | null {
const start = this._lexer.get();
let props: (SpreadExpression | [Expression, Expression])[] = [];
if (this._lexer.peek().type !== TokenType.RBrace) {
while (this._lexer.peek().type !== TokenType.RBrace) {
// --- Check the next token
const nextToken = this._lexer.peek();
const traits = tokenTraits[nextToken.type];
let nameExpr: Expression | null;
// --- Get property name or calculated property name
if (traits.expressionStart) {
if (nextToken.type === TokenType.LSquare) {
this._lexer.get();
nameExpr = this.getExpression();
if (!nameExpr) {
return null;
}
this.expectToken(TokenType.RSquare, "W005");
nameExpr = this.createExpressionNode<SequenceExpression>(
T_SEQUENCE_EXPRESSION,
{
exprs: [nameExpr],
},
start,
);
} else if (traits.isPropLiteral) {
nameExpr = this.getExpression(false);
if (!nameExpr) {
return null;
}
if (
nameExpr.type !== T_IDENTIFIER &&
nameExpr.type !== T_LITERAL &&
nameExpr.type !== T_SPREAD_EXPRESSION
) {
this.reportError("W007");
return null;
}
} else {
this.reportError("W007");
return null;
}
} else if (traits.keywordLike) {
nameExpr = {
type: T_IDENTIFIER,
nodeId: createXmlUiTreeNodeId(),
name: nextToken.text,
startToken: nextToken,
endToken: nextToken,
};
this._lexer.get();
} else {
this.reportError("W001");
return null;
}
const nameType = nameExpr.type;
if (nameType === T_SPREAD_EXPRESSION) {
props.push(nameExpr);
} else {
if (nameType === T_LITERAL) {
const val = nameExpr.value;
if (typeof val !== "number" && typeof val !== "string") {
this.expectToken(TokenType.RBrace, "W007");
return null;
}
}
// --- Value is optional, when we have a name
let valueExpr: Expression | null = null;
if (nameType === T_IDENTIFIER) {
const nameFollowerToken = this._lexer.peek();
if (
nameFollowerToken.type === TokenType.Comma ||
nameFollowerToken.type === TokenType.RBrace
) {
valueExpr = { ...nameExpr };
}
}
// --- Move to property value
if (!valueExpr) {
this.expectToken(TokenType.Colon, "W008");
valueExpr = this.getExpression(false);
if (!valueExpr) {
return null;
}
}
props.push([nameExpr, valueExpr]);
}
// --- Test property termination
const next = this._lexer.peek().type;
if (next === TokenType.Comma) {
this._lexer.get();
} else {
if (next !== TokenType.RBrace) {
break;
}
}
}
}
const endToken = this._lexer.peek();
this.expectToken(TokenType.RBrace, "W004");
return this.createExpressionNode<ObjectLiteral>(
T_OBJECT_LITERAL,
{
props,
},
start,
endToken,
);
}
private parseRegExpLiteral(): Literal | null {
const startToken = this._lexer.peek();
const result = this._lexer.getRegEx();
if (result.success) {
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value: new RegExp(result.pattern!, result.flags),
},
startToken,
this._lexer.peek(),
);
}
this.reportError("W002", startToken, result.pattern ?? "");
return null;
}
/**
* Gets an expression
*/
private getExpression(allowSequence = true): Expression | null {
const expr = this.parseExpr(allowSequence);
if (expr) {
return expr;
}
this.reportError("W001");
return null;
}
// ==========================================================================
// Helpers
/**
* Tests the type of the next token
* @param type Expected token type
* @param errorCode Error to raise if the next token is not expected
* @param allowEof Allow an EOF instead of the expected token?
*/
private expectToken(type: TokenType, errorCode?: ErrorCodes, allowEof?: boolean): Token | null {
const next = this._lexer.peek();
if (next.type === type || (allowEof && next.type === TokenType.Eof)) {
// --- Skip the expected token
return this._lexer.get();
}
this.reportError(errorCode ?? "W002", next, next.text);
return null;
}
/**
* Skips the next token if the type is the specified one
* @param type Token type to check
*/
private skipToken(type: TokenType): Token | null {
const next = this._lexer.peek();
if (next.type === type) {
this._lexer.get();
return next;
}
return null;
}
/**
* Skips the next token if the type is the specified one
* @param types Token types to check
*/
private skipTokens(...types: TokenType[]): Token | null {
const next = this._lexer.peek();
for (const type of types) {
if (next.type === type) {
this._lexer.get();
return next;
}
}
return null;
}
/**
* Reports the specified error
* @param errorCode Error code
* @param token Token that represents the error's position
* @param options Error message options
*/
private reportError(errorCode: ErrorCodes, token?: Token, ...options: any[]): void {
let errorText: string = errorMessages[errorCode] ?? "Unkonwn error";
if (options) {
options.forEach(
(o, idx) => (errorText = replace(errorText, `{${idx}}`, options[idx].toString())),
);
}
if (!token) {
token = this._lexer.peek();
}
this._parseErrors.push({
code: errorCode,
text: errorText,
line: token.startLine,
column: token.startColumn,
});
throw new ParserError(errorText, errorCode);
function replace(input: string, placeholder: string, replacement: string): string {
do {
input = input.replace(placeholder, replacement);
} while (input.includes(placeholder));
return input;
}
}
/**
* Creates an expression node
* @param type Expression type
* @param stump Stump properties
* @param startToken The token that starts the expression
* @param endToken The token that ends the expression
* @param source Expression source code to store to the node
*/
private createNode<T extends ScripNodeBase>(
type: ScripNodeBase["type"],
stump: any,
startToken: Token,
endToken?: Token,
): T {
if (!endToken) {
endToken = this._lexer.peek();
}
return Object.assign({}, stump, {
type,
startToken,
endToken,
} as ScripNodeBase);
}
/**
* Creates an expression node
* @param type Expression type
* @param stump Stump properties
* @param startToken The token that starts the expression
* @param endToken The token that ends the expression
* @param source Expression source code to store to the node
*/
private createExpressionNode<T extends Expression>(
type: Expression["type"],
stump: any = {},
startToken?: Token,
endToken?: Token,
): T {
if (!endToken) {
endToken = this._lexer.peek();
}
if (!startToken) {
startToken = endToken;
}
return Object.assign({}, stump, {
type,
nodeId: createXmlUiTreeNodeId(),
startToken,
endToken,
});
}
/**
* Creates a statement node
* @param type Statement type
* @param stump Stump properties
* @param startToken The token that starts the statement
* @param endToken The token that ends the statement
*/
private createStatementNode<T extends Statement>(
type: Statement["type"],
stump: any,
startToken?: Token,
endToken?: Token,
): T {
return Object.assign({}, stump, {
type,
nodeId: createXmlUiTreeNodeId(),
startToken,
endToken,
} as Statement);
}
/**
* Parses a binary literal
* @param token Literal token
*/
private parseBinaryLiteral(token: Token): Literal {
let value: number | bigint;
const bigValue = BigInt(token.text.replace(/[_']/g, ""));
if (bigValue < Number.MIN_SAFE_INTEGER || bigValue > Number.MAX_SAFE_INTEGER) {
value = bigValue;
} else {
value = parseInt(token.text.substring(2).replace(/[_']/g, ""), 2);
}
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value,
},
token,
token,
);
}
/**
* Parses a decimal literal
* @param token Literal token
*/
private parseDecimalLiteral(token: Token): Literal {
let value: number | bigint;
const bigValue = BigInt(token.text.replace(/[_']/g, ""));
if (bigValue < Number.MIN_SAFE_INTEGER || bigValue > Number.MAX_SAFE_INTEGER) {
value = bigValue;
} else {
value = parseInt(token.text.replace(/[_']/g, ""), 10);
}
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value,
},
token,
token,
);
}
/**
* Parses a hexadecimal literal
* @param token Literal token
*/
private parseHexadecimalLiteral(token: Token): Literal {
let value: number | bigint;
const bigValue = BigInt(token.text.replace(/[_']/g, ""));
if (bigValue < Number.MIN_SAFE_INTEGER || bigValue > Number.MAX_SAFE_INTEGER) {
value = bigValue;
} else {
value = parseInt(token.text.substring(2).replace(/[_']/g, ""), 16);
}
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value,
},
token,
token,
);
}
/**
* Parses a real literal
* @param token Literal token
*/
private parseRealLiteral(token: Token): Literal {
let value = parseFloat(token.text.replace(/[_']/g, ""));
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value,
},
token,
token,
);
}
/**
* Converts a string token to intrinsic string
* @param token Literal token
*/
private parseStringLiteral(token: Token, quoteSurrounded: boolean = true): Literal {
let input = token.text;
if (quoteSurrounded) {
input = token.text.length < 2 ? "" : input.substring(1, input.length - 1);
}
let result = "";
let state: StrParseState = StrParseState.Normal;
let collect = 0;
for (const ch of input) {
switch (state) {
case StrParseState.Normal:
if (ch === "\\") {
state = StrParseState.Backslash;
} else {
result += ch;
}
break;
case StrParseState.Backslash:
state = StrParseState.Normal;
switch (ch) {
case "b":
result += "\b";
break;
case "f":
result += "\f";
break;
case "n":
result += "\n";
break;
case "r":
result += "\r";
break;
case "t":
result += "\t";
break;
case "v":
result += "\v";
break;
case "S":
result += "\xa0";
break;
case "0":
result += String.fromCharCode(0x00);
break;
case "'":
result += "'";
break;
case '"':
result += '"';
break;
case "\\":
result += "\\";
break;
case "x":
state = StrParseState.X;
break;
case "u":
state = StrParseState.UX1;
break;
default:
result += ch;
break;
}
break;
case StrParseState.X:
if (isHexaDecimal(ch)) {
collect = parseInt(ch, 16);
state = StrParseState.Xh;
} else {
result += "x";
state = StrParseState.Normal;
}
break;
case StrParseState.Xh:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
result += String.fromCharCode(collect);
state = StrParseState.Normal;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.UX1:
if (ch === "{") {
state = StrParseState.Ucp1;
break;
}
if (isHexaDecimal(ch)) {
collect = parseInt(ch, 16);
state = StrParseState.UX2;
} else {
result += "x";
state = StrParseState.Normal;
}
break;
case StrParseState.UX2:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
state = StrParseState.UX3;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.UX3:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
state = StrParseState.UX4;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.UX4:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
result += String.fromCharCode(collect);
state = StrParseState.Normal;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.Ucp1:
if (isHexaDecimal(ch)) {
collect = parseInt(ch, 16);
state = StrParseState.Ucp2;
} else {
result += "x";
state = StrParseState.Normal;
}
break;
case StrParseState.Ucp2:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
state = StrParseState.Ucp3;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.Ucp3:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
state = StrParseState.Ucp4;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.Ucp4:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
state = StrParseState.Ucp5;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.Ucp5:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
state = StrParseState.Ucp6;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.Ucp6:
if (isHexaDecimal(ch)) {
collect = collect * 0x10 + parseInt(ch, 16);
state = StrParseState.UcpTail;
} else {
result += String.fromCharCode(collect);
result += ch;
state = StrParseState.Normal;
}
break;
case StrParseState.UcpTail:
result += String.fromCharCode(collect);
if (ch !== "}") {
result += ch;
}
state = StrParseState.Normal;
break;
}
}
// --- Handle the final machine state
switch (state) {
case StrParseState.Backslash:
result += "\\";
break;
case StrParseState.X:
result += "x";
break;
case StrParseState.Xh:
result += String.fromCharCode(collect);
break;
}
// --- Done
return this.createExpressionNode<Literal>(
T_LITERAL,
{
value: result,
},
token,
token,
);
function isHexaDecimal(ch: string): boolean {
return (ch >= "0" && ch <= "9") || (ch >= "a" && ch <= "f") || (ch >= "A" && ch <= "F");
}
}
private convertToArrayDestructure(seq: SequenceExpression | ArrayLiteral): Destructure | null {
const items = seq.type === T_SEQUENCE_EXPRESSION ? seq.exprs : seq.items;
const result = this.createExpressionNode<Destructure>(
T_DESTRUCTURE,
{ aDestr: [] },
seq.startToken,
seq.endToken,
);
// --- Convert all items
for (const item of items) {
let arrayD: ArrayDestructure | undefined;
switch (item.type) {
case T_NO_ARG_EXPRESSION:
arrayD = this.createExpressionNode<ArrayDestructure>(
T_ARRAY_DESTRUCTURE,
{},
item.startToken,
item.endToken,
);
break;
case T_IDENTIFIER:
arrayD = this.createExpressionNode<ArrayDestructure>(
T_ARRAY_DESTRUCTURE,
{ id: item.name },
item.startToken,
item.endToken,
);
break;
case T_DESTRUCTURE:
result.aDestr!.push(...item.aDestr!);
break;
case T_ARRAY_DESTRUCTURE:
arrayD = item;
break;
case T_ARRAY_LITERAL: {
const destructure = this.convertToArrayDestructure(item);
if (destructure) {
arrayD = this.createExpressionNode<ArrayDestructure>(
T_ARRAY_DESTRUCTURE,
{
aDestr: destructure.aDestr,
},
item.startToken,
item.endToken,
);
}
break;
}
case T_OBJECT_DESTRUCTURE:
arrayD = this.createExpressionNode<ArrayDestructure>(
T_ARRAY_DESTRUCTURE,
{
oDestr: item,
},
item.startToken,
item.endToken,
);
break;
case T_OBJECT_LITERAL: {
const destructure = this.convertToObjectDestructure(item);
if (destructure) {
arrayD = this.createExpressionNode<ArrayDestructure>(
T_ARRAY_DESTRUCTURE,
{
oDestr: destructure.oDestr,
},
item.startToken,
item.endToken,
);
}
break;
}
default:
this.reportError("W017");
return null;
}
if (arrayD) result.aDestr?.push(arrayD);
}
// --- Done.
return result;
}
private convertToObjectDestructure(objLit: ObjectLiteral): Destructure | null {
const result = this.createExpressionNode<Destructure>(
T_DESTRUCTURE,
{ oDestr: [] },
objLit.startToken,
objLit.endToken,
);
// --- Convert all items
for (const prop of objLit.props) {
if (Array.isArray(prop)) {
} else {
reportError("W018");
return null;
}
const [propKey, propValue] = prop;
if (propKey.type !== T_IDENTIFIER) {
reportError("W018");
return null;
}
let objD: ObjectDestructure | undefined;
switch (propValue.type) {
case T_IDENTIFIER:
if (propValue.name === propKey.name) {
objD = this.createExpressionNode<ObjectDestructure>(
T_OBJECT_DESTRUCTURE,
{ id: propKey.name },
propValue.startToken,
propValue.endToken,
);
} else {
objD = this.createExpressionNode<ObjectDestructure>(
T_OBJECT_DESTRUCTURE,
{
id: propKey.name,
alias: propValue.name,
},
propValue.startToken,
propValue.endToken,
);
}
break;
case T_ARRAY_DESTRUCTURE: {
objD = this.createExpressionNode<ObjectDestructure>(
T_OBJECT_DESTRUCTURE,
{
id: propKey.name,
aDestr: propValue,
},
propKey.startToken,
propValue.endToken,
);
break;
}
case T_ARRAY_LITERAL: {
const destructure = this.convertToArrayDestructure(propValue);
if (destructure) {
objD = this.createExpressionNode<ObjectDestructure>(
T_OBJECT_DESTRUCTURE,
{
id: propKey.name,
aDestr: destructure.aDestr,
},
propKey.startToken,
propValue.endToken,
);
}
break;
}
case T_OBJECT_DESTRUCTURE:
objD = propValue;
break;
case T_OBJECT_LITERAL: {
const destructure = this.convertToObjectDestructure(propValue);
if (destructure) {
objD = this.createExpressionNode<ObjectDestructure>(
T_OBJECT_DESTRUCTURE,
{
id: propKey.name,
oDestr: destructure.oDestr,
},
propKey.startToken,
propValue.endToken,
);
}
break;
}
default:
this.reportError("W018");
return null;
}
if (objD) result.oDestr?.push(objD);
}
// --- Done.
return result;
}
/**
* Tests if the specified token can be the start of an expression
*/
private isExpressionStart(token: Token): boolean {
return tokenTraits[token.type]?.expressionStart ?? false;
}
}
```