Repository: xiaoxinghu/orgajs Branch: main Commit: 511bbeef8998 Files: 439 Total size: 820.0 KB Directory structure: gitextract_ms15wv5a/ ├── .changeset/ │ ├── README.md │ └── config.json ├── .editorconfig ├── .eslintignore ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── ci.yml │ ├── release.yml │ └── website.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CONTRIBUTING.org ├── LICENSE.org ├── README.org ├── biome.json ├── docs/ │ ├── .gitignore │ ├── _components.tsx │ ├── _layout.tsx │ ├── _snippets/ │ │ └── hey.org │ ├── advanced/ │ │ ├── _layout.tsx │ │ ├── api.org │ │ ├── ast.org │ │ ├── index.org │ │ └── latex.org │ ├── contribute.org │ ├── guides/ │ │ ├── _layout.tsx │ │ ├── astro.org │ │ ├── gatsby.org │ │ ├── index.org │ │ ├── next.org │ │ ├── orga-build.org │ │ └── webpack.org │ ├── index.org │ ├── playground.tsx │ ├── style.css │ └── tsconfig.json ├── examples/ │ ├── README.org │ ├── build/ │ │ ├── index.org │ │ ├── more.org │ │ ├── package.json │ │ └── test-content.tsx │ ├── editor/ │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── content.org │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── main.ts │ │ │ └── style.css │ │ └── tsconfig.json │ ├── getting-started/ │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── README.org │ │ ├── index.js │ │ └── package.json │ └── webpack/ │ ├── .babelrc │ ├── CHANGELOG.md │ ├── README.org │ ├── package.json │ ├── public/ │ │ └── index.html │ ├── src/ │ │ ├── box.js │ │ ├── hello.org │ │ └── index.js │ └── webpack.config.mjs ├── orga.config.js ├── package.json ├── packages/ │ ├── astro/ │ │ └── README.org │ ├── codemirror-lang/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ ├── package.json │ │ └── tsconfig.json │ ├── editor/ │ │ ├── CHANGELOG.md │ │ ├── README.org │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── actions/ │ │ │ │ ├── fold.js │ │ │ │ ├── shift.js │ │ │ │ ├── todo.js │ │ │ │ └── utils.js │ │ │ ├── editor.js │ │ │ ├── extensions/ │ │ │ │ └── cleanup.js │ │ │ ├── settings.js │ │ │ ├── setup.js │ │ │ └── theme.js │ │ ├── package.json │ │ └── tsconfig.json │ ├── esbuild/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ └── package.json │ ├── lezer/ │ │ ├── CHANGELOG.md │ │ ├── README.org │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── context.js │ │ │ ├── fragments.js │ │ │ ├── handlers.js │ │ │ ├── index.js │ │ │ ├── nodes.js │ │ │ ├── oast-to-lezer.js │ │ │ ├── tree.js │ │ │ └── types.ts │ │ ├── package.json │ │ ├── tests/ │ │ │ ├── compare-tree.js │ │ │ └── incremental.test.js │ │ └── tsconfig.json │ ├── loader/ │ │ ├── CHANGELOG.md │ │ ├── LICENSE.org │ │ ├── index.cjs │ │ ├── index.d.ts │ │ ├── lib/ │ │ │ └── index.js │ │ ├── package.json │ │ └── tests/ │ │ ├── compiler.js │ │ ├── example.md │ │ ├── fixture.org │ │ └── loader.test.js │ ├── metadata/ │ │ ├── CHANGELOG.md │ │ ├── README.org │ │ ├── index.js │ │ ├── lib/ │ │ │ └── index.js │ │ ├── package.json │ │ ├── tests/ │ │ │ └── test.js │ │ └── tsconfig.json │ ├── next/ │ │ └── README.org │ ├── node-loader/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ └── package.json │ ├── oast-to-hast/ │ │ ├── .projectile │ │ ├── CHANGELOG.md │ │ ├── LICENSE.org │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── handlers/ │ │ │ │ ├── block.js │ │ │ │ ├── document.js │ │ │ │ ├── footnote.js │ │ │ │ ├── headline.js │ │ │ │ ├── hr.js │ │ │ │ ├── html.js │ │ │ │ ├── index.js │ │ │ │ ├── keyword.js │ │ │ │ ├── latex.js │ │ │ │ ├── link.js │ │ │ │ ├── list.js │ │ │ │ ├── newline.js │ │ │ │ ├── paragraph.js │ │ │ │ ├── section.js │ │ │ │ ├── table.js │ │ │ │ └── text.js │ │ │ ├── index.js │ │ │ └── state.js │ │ ├── package.json │ │ ├── tests/ │ │ │ ├── block.js │ │ │ ├── classname.js │ │ │ ├── heading.js │ │ │ ├── list.js │ │ │ └── paragraph.js │ │ └── tsconfig.json │ ├── oast-to-prose/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── handlers/ │ │ │ │ ├── block.js │ │ │ │ ├── headline.js │ │ │ │ ├── index.js │ │ │ │ ├── link.js │ │ │ │ ├── newline.js │ │ │ │ ├── paragraph.js │ │ │ │ ├── section.js │ │ │ │ ├── stars.js │ │ │ │ ├── tags.js │ │ │ │ ├── text.js │ │ │ │ └── todo.js │ │ │ ├── index.js │ │ │ ├── schema.js │ │ │ └── state.js │ │ ├── package.json │ │ ├── tests/ │ │ │ └── text.js │ │ └── tsconfig.json │ ├── orga/ │ │ ├── .projectile │ │ ├── CHANGELOG.md │ │ ├── LICENSE.org │ │ ├── README.org │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ ├── block/ │ │ │ │ │ ├── affiliated keyword.json │ │ │ │ │ ├── affiliated keyword.org │ │ │ │ │ ├── export.json │ │ │ │ │ ├── export.org │ │ │ │ │ ├── missing begin.json │ │ │ │ │ ├── missing begin.org │ │ │ │ │ ├── missing end.json │ │ │ │ │ ├── missing end.org │ │ │ │ │ ├── standard.json │ │ │ │ │ └── standard.org │ │ │ │ ├── footnote/ │ │ │ │ │ ├── multiline.json │ │ │ │ │ ├── multiline.org │ │ │ │ │ ├── standard.json │ │ │ │ │ ├── standard.org │ │ │ │ │ ├── stopped by empty lines.json │ │ │ │ │ ├── stopped by empty lines.org │ │ │ │ │ ├── stopped by footnote.json │ │ │ │ │ ├── stopped by footnote.org │ │ │ │ │ ├── stopped by headline.json │ │ │ │ │ ├── stopped by headline.org │ │ │ │ │ ├── with block.json │ │ │ │ │ └── with block.org │ │ │ │ ├── headline/ │ │ │ │ │ ├── broken drawer.json │ │ │ │ │ ├── broken drawer.org │ │ │ │ │ ├── drawers.json │ │ │ │ │ ├── drawers.org │ │ │ │ │ ├── keyword.json │ │ │ │ │ ├── keyword.org │ │ │ │ │ ├── nested.json │ │ │ │ │ ├── nested.org │ │ │ │ │ ├── planning.json │ │ │ │ │ ├── planning.org │ │ │ │ │ ├── with tags.json │ │ │ │ │ └── with tags.org │ │ │ │ ├── hr/ │ │ │ │ │ ├── standard.json │ │ │ │ │ └── standard.org │ │ │ │ ├── index.test.ts │ │ │ │ ├── keyword/ │ │ │ │ │ ├── multiple todo.json │ │ │ │ │ ├── multiple todo.org │ │ │ │ │ ├── other.json │ │ │ │ │ ├── other.org │ │ │ │ │ ├── todo.json │ │ │ │ │ └── todo.org │ │ │ │ ├── latex/ │ │ │ │ │ ├── does not match.json │ │ │ │ │ ├── does not match.org │ │ │ │ │ ├── latex block.json │ │ │ │ │ ├── latex block.org │ │ │ │ │ ├── missing begin.json │ │ │ │ │ ├── missing begin.org │ │ │ │ │ ├── missing end.json │ │ │ │ │ └── missing end.org │ │ │ │ ├── list/ │ │ │ │ │ ├── broken by empty line.json │ │ │ │ │ ├── broken by empty line.org │ │ │ │ │ ├── descriptive.json │ │ │ │ │ ├── descriptive.org │ │ │ │ │ ├── multiline.json │ │ │ │ │ ├── multiline.org │ │ │ │ │ ├── nested.json │ │ │ │ │ ├── nested.org │ │ │ │ │ ├── ordered.json │ │ │ │ │ ├── ordered.org │ │ │ │ │ ├── unordered.json │ │ │ │ │ └── unordered.org │ │ │ │ ├── paragraph/ │ │ │ │ │ ├── anonymous footnote.json │ │ │ │ │ ├── anonymous footnote.org │ │ │ │ │ ├── footnote reference at the end.json │ │ │ │ │ ├── footnote reference at the end.org │ │ │ │ │ ├── footnote refernece in the middle.json │ │ │ │ │ ├── footnote refernece in the middle.org │ │ │ │ │ ├── footnote without body.json │ │ │ │ │ ├── footnote without body.org │ │ │ │ │ ├── inline footnote with style.json │ │ │ │ │ ├── inline footnote with style.org │ │ │ │ │ ├── inline footnote.json │ │ │ │ │ ├── inline footnote.org │ │ │ │ │ ├── inline math.json │ │ │ │ │ ├── inline math.org │ │ │ │ │ ├── link with style.json │ │ │ │ │ ├── link with style.org │ │ │ │ │ ├── link.json │ │ │ │ │ ├── link.org │ │ │ │ │ ├── nested footnote.json │ │ │ │ │ ├── nested footnote.org │ │ │ │ │ ├── styled text.json │ │ │ │ │ └── styled text.org │ │ │ │ └── table/ │ │ │ │ ├── standard.json │ │ │ │ ├── standard.org │ │ │ │ ├── with inline styles.json │ │ │ │ └── with inline styles.org │ │ │ ├── index.ts │ │ │ ├── nodes.ts │ │ │ ├── options.ts │ │ │ ├── parse/ │ │ │ │ ├── _parseSymbols.ts │ │ │ │ ├── _primitive.ts │ │ │ │ ├── _utils.ts │ │ │ │ ├── block.ts │ │ │ │ ├── context.ts │ │ │ │ ├── drawer.ts │ │ │ │ ├── footnote.ts │ │ │ │ ├── headline.ts │ │ │ │ ├── index.ts │ │ │ │ ├── keyword.ts │ │ │ │ ├── latex.ts │ │ │ │ ├── list.ts │ │ │ │ ├── paragraph.ts │ │ │ │ ├── phrasing.ts │ │ │ │ ├── planning.ts │ │ │ │ ├── section.ts │ │ │ │ └── table.ts │ │ │ ├── position.ts │ │ │ ├── timestamp.test.ts │ │ │ ├── timestamp.ts │ │ │ ├── todo.test.ts │ │ │ ├── todo.ts │ │ │ ├── tokenize/ │ │ │ │ ├── __tests__/ │ │ │ │ │ ├── debug.ts │ │ │ │ │ └── tok.ts │ │ │ │ ├── blank.test.ts │ │ │ │ ├── block.test.ts │ │ │ │ ├── block.ts │ │ │ │ ├── comment.test.ts │ │ │ │ ├── comment.ts │ │ │ │ ├── drawer.test.ts │ │ │ │ ├── drawer.ts │ │ │ │ ├── empty.ts │ │ │ │ ├── footnote.test.ts │ │ │ │ ├── footnote.ts │ │ │ │ ├── headline.test.ts │ │ │ │ ├── headline.ts │ │ │ │ ├── hr.test.ts │ │ │ │ ├── hr.ts │ │ │ │ ├── index.ts │ │ │ │ ├── inline/ │ │ │ │ │ ├── footnote.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── link.ts │ │ │ │ │ ├── math.ts │ │ │ │ │ └── text.ts │ │ │ │ ├── inline.test.ts │ │ │ │ ├── keyword.ts │ │ │ │ ├── keywords.test.ts │ │ │ │ ├── latex.ts │ │ │ │ ├── list.test.ts │ │ │ │ ├── list.ts │ │ │ │ ├── partial.test.ts │ │ │ │ ├── planning.test.ts │ │ │ │ ├── planning.ts │ │ │ │ ├── table.test.ts │ │ │ │ └── table.ts │ │ │ ├── types.ts │ │ │ ├── uri.test.ts │ │ │ ├── uri.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── orga-build/ │ │ ├── CHANGELOG.md │ │ ├── README.org │ │ ├── cli.js │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── __tests__/ │ │ │ │ └── build.test.js │ │ │ ├── app.jsx │ │ │ ├── build.js │ │ │ ├── components.js │ │ │ ├── config.js │ │ │ ├── content.d.ts │ │ │ ├── csr.jsx │ │ │ ├── endpoint.js │ │ │ ├── files.js │ │ │ ├── fs.js │ │ │ ├── index.html │ │ │ ├── orga.js │ │ │ ├── plugin.js │ │ │ ├── serve.js │ │ │ ├── ssr.jsx │ │ │ ├── util.js │ │ │ ├── vite.js │ │ │ └── watch.js │ │ ├── package.json │ │ └── tsconfig.json │ ├── orgx/ │ │ ├── CHANGELOG.md │ │ ├── README.org │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── compile.js │ │ │ ├── core.js │ │ │ ├── evaluate.js │ │ │ ├── plugin/ │ │ │ │ ├── recma-build-jsx-transform.js │ │ │ │ ├── recma-document.js │ │ │ │ ├── recma-jsx-rewrite.js │ │ │ │ └── rehype-recma.js │ │ │ ├── run.js │ │ │ ├── types.ts │ │ │ └── util/ │ │ │ ├── estree-util-create.js │ │ │ ├── estree-util-declaration-to-expression.js │ │ │ ├── estree-util-is-declaration.js │ │ │ ├── estree-util-specifiers-to-declarations.js │ │ │ ├── estree-util-to-binary-addition.js │ │ │ ├── estree-util-to-id-or-member-expression.js │ │ │ ├── is-org-content.js │ │ │ ├── render-error.js │ │ │ ├── resolve-evaluate-options.js │ │ │ └── resolve-file-and-options.js │ │ ├── package.json │ │ ├── tests/ │ │ │ ├── compile.test.js │ │ │ └── evaluate.test.js │ │ └── tsconfig.json │ ├── react/ │ │ ├── CHANGELOG.md │ │ ├── LICENSE.org │ │ ├── index.js │ │ ├── package.json │ │ ├── tests/ │ │ │ ├── remove-export-keywords.js │ │ │ └── test.tsx │ │ └── tsconfig.json │ ├── react-cm/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ └── package.json │ ├── react-editor/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ ├── package.json │ │ └── tsconfig.json │ ├── reorg/ │ │ ├── CHANGELOG.md │ │ ├── LICENSE.org │ │ ├── README.org │ │ ├── index.js │ │ └── package.json │ ├── reorg-parse/ │ │ ├── CHANGELOG.md │ │ ├── LICENSE.org │ │ ├── README.org │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── package.json │ │ └── tsconfig.json │ ├── reorg-prose/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ ├── lib/ │ │ │ └── index.js │ │ ├── package.json │ │ └── tsconfig.json │ ├── reorg-rehype/ │ │ ├── .projectile │ │ ├── CHANGELOG.md │ │ ├── LICENSE.org │ │ ├── index.js │ │ ├── package.json │ │ └── tsconfig.json │ ├── rollup/ │ │ ├── CHANGELOG.md │ │ ├── index.js │ │ ├── package.json │ │ ├── test.js │ │ └── tsconfig.json │ └── text-kit/ │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib/ │ │ ├── core.js │ │ ├── reader.js │ │ └── utils/ │ │ ├── index.js │ │ ├── lines.js │ │ └── substring.js │ ├── package.json │ ├── tests/ │ │ ├── core.js │ │ └── lines.js │ └── tsconfig.json ├── pnpm-workspace.yaml ├── shell.nix ├── tsconfig.base.json ├── tsconfig.esm.json ├── tsconfig.json └── tsconfig.packages.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .changeset/README.md ================================================ # Changesets Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets) We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) ================================================ FILE: .changeset/config.json ================================================ { "$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json", "changelog": "@changesets/cli/changelog", "commit": false, "linked": [], "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [] } ================================================ FILE: .editorconfig ================================================ root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = tab [*.{yml,yaml}] indent_style = space indent_size = 2 [vcbuild.bat] end_of_line = crlf [Makefile] indent_style = tab indent_size = 8 [{deps}/**] indent_style = ignore indent_size = ignore end_of_line = ignore trim_trailing_whitespace = ignore charset = ignore [{test/fixtures,deps,tools/node_modules,tools/gyp,tools/icu,tools/msvs}/**] insert_final_newline = false ================================================ FILE: .eslintignore ================================================ node_modules **/dist/** **/__tests__/**/*.json ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: ['xiaoxinghu'] ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: - main paths-ignore: - 'docs/**' - '**/README.org' - '**/README.md' pull_request: paths-ignore: - 'docs/**' - '**/README.org' - '**/README.md' jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v4 with: node-version: 24 - uses: pnpm/action-setup@v4 with: version: 10 cache: 'pnpm' - name: install dependencies run: pnpm install - name: build packages run: pnpm build - name: run unit tests run: pnpm test ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: branches: - main env: CI: true permissions: {} jobs: release: name: Release runs-on: ubuntu-latest permissions: id-token: write # Required for OIDC trusted publishing contents: write # Required for changesets to commit pull-requests: write # Required for changesets to create PRs steps: - name: Checkout Repo uses: actions/checkout@v4 with: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 - name: Setup Node.js 24 uses: actions/setup-node@v4 with: node-version: 24 - uses: pnpm/action-setup@v4 with: version: 10 - name: install dependencies run: pnpm install --no-frozen-lockfile - name: Create Release Pull Request or Publish to npm uses: changesets/action@v1 with: version: pnpm ci:version commit: 'chore: update versions' title: 'chore: update versions' publish: pnpm ci:publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_CONFIG_PROVENANCE: true ================================================ FILE: .github/workflows/website.yml ================================================ name: Deploy to GitHub Pages on: # Trigger the workflow every time you push to the `main` branch # Using a different branch name? Replace `main` with your branch’s name push: branches: [main] # Allows you to run this workflow manually from the Actions tab on GitHub. workflow_dispatch: # Allow this job to clone the repo and create a page deployment permissions: contents: read pages: write id-token: write jobs: build: runs-on: ubuntu-latest steps: - name: Checkout your repository using git uses: actions/checkout@v3 - name: Setup PNPM uses: pnpm/action-setup@v4 with: version: 10 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Install run: | pnpm install pnpm run build pnpm run docs - name: Upload Page Artifact uses: actions/upload-pages-artifact@v3 with: path: '.out/' deploy: needs: build # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: pages: write # to deploy to Pages id-token: write # to verify the deployment originates from an appropriate source environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .gitignore ================================================ # macOS # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Typescript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # next.js build output .next .npmrc # private .archive /.idea .vscode # build artifacts dist *.tsbuildinfo *.d.ts *.d.ts.map !packages/reorg-parse/index.d.ts !packages/loader/index.d.ts !packages/orga-build/lib/content.d.ts .meta.json .orga-build .turbo .aider* .out .direnv .website deprecated ================================================ FILE: .prettierignore ================================================ .archive .cache node_modules .next public lib **/dist/** .yarn/* **/__tests__/**/*.json ================================================ FILE: .prettierrc ================================================ { "singleQuote": true, "semi": false, "trailingComma": "none", "bracketSpacing": true, "useTabs": true } ================================================ FILE: CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. # [2.6.0](https://github.com/orgapp/orgajs/compare/v2.5.0...v2.6.0) (2021-08-28) - feat!: major version bump ([998c0f3](https://github.com/orgapp/orgajs/commit/998c0f30a7c8f9af27243b3cb48b650708cdc4b3)) ### Bug Fixes - fix examples ([bdcd265](https://github.com/orgapp/orgajs/commit/bdcd2655502a73800e8915ba09fd78452dff503f)) - fix headline regex issue ([a36b75d](https://github.com/orgapp/orgajs/commit/a36b75d87da125f56edf7da1ddaf23771040ce1b)) - fix headline tags parsing issue [#126](https://github.com/orgapp/orgajs/issues/126) ([71d7f82](https://github.com/orgapp/orgajs/commit/71d7f8277708fc72d3b5be01ed0f72233bf7057b)) - fix phrasing content in headline ([31ca41c](https://github.com/orgapp/orgajs/commit/31ca41cb3b9b65a19dbc71a906f86ee4d725ad8f)) - fix publish script ([7fc1bdf](https://github.com/orgapp/orgajs/commit/7fc1bdfc6880e825cd10d99d108da9685f58638c)) - fix publish script ([f175e5b](https://github.com/orgapp/orgajs/commit/f175e5bb3cad0ce31cc118c1c675fe30b4065fc9)) - inline markup check post ([d3d31c6](https://github.com/orgapp/orgajs/commit/d3d31c622dde2a2d469ac41884f2320497f811c6)) - lock unified & @types/unist version ([dc72217](https://github.com/orgapp/orgajs/commit/dc72217f0cfcd778436d704021116c8479f8ee1e)) - remove prepublish step individually ([a75a6a9](https://github.com/orgapp/orgajs/commit/a75a6a9606421b66b6ef69b28e3fcb03a5ee282a)) - **website:** better code block ([9d5b3a2](https://github.com/orgapp/orgajs/commit/9d5b3a2d554672d22523727e89b2b5c60dc6233d)) - update yarn.lock file ([bf0e695](https://github.com/orgapp/orgajs/commit/bf0e6954246f7ed78a014601aca756cd47fa4fec)) ### Features - add jsx support ([0d22499](https://github.com/orgapp/orgajs/commit/0d224990b412e064ebf6816608eea6766f93d60c)) - better code block in website ([3efe4cd](https://github.com/orgapp/orgajs/commit/3efe4cd96a63623e2f70028bd66346960ec90bec)) - **gatsby:** process images ([76fdade](https://github.com/orgapp/orgajs/commit/76fdaded0d87a3bc2e188392b520f62ad789598c)) ### BREAKING CHANGES - due to my own lack of knowledge about how conventional commits works in lerna, I published the breaking change as a minor version 2.5.0. Here I am trying to get it right. It might not work... Sorry for the inconvenience. ## 2.4.9 (2021-07-13) # [2.5.0](https://github.com/orgapp/orgajs/compare/v2.4.9...v2.5.0) (2021-08-27) ### Bug Fixes - fix examples ([bdcd265](https://github.com/orgapp/orgajs/commit/bdcd2655502a73800e8915ba09fd78452dff503f)) - fix headline regex issue ([a36b75d](https://github.com/orgapp/orgajs/commit/a36b75d87da125f56edf7da1ddaf23771040ce1b)) - fix phrasing content in headline ([31ca41c](https://github.com/orgapp/orgajs/commit/31ca41cb3b9b65a19dbc71a906f86ee4d725ad8f)) - fix publish script ([7fc1bdf](https://github.com/orgapp/orgajs/commit/7fc1bdfc6880e825cd10d99d108da9685f58638c)) - fix publish script ([f175e5b](https://github.com/orgapp/orgajs/commit/f175e5bb3cad0ce31cc118c1c675fe30b4065fc9)) - inline markup check post ([d3d31c6](https://github.com/orgapp/orgajs/commit/d3d31c622dde2a2d469ac41884f2320497f811c6)) - **website:** better code block ([9d5b3a2](https://github.com/orgapp/orgajs/commit/9d5b3a2d554672d22523727e89b2b5c60dc6233d)) - lock unified & @types/unist version ([dc72217](https://github.com/orgapp/orgajs/commit/dc72217f0cfcd778436d704021116c8479f8ee1e)) - update yarn.lock file ([bf0e695](https://github.com/orgapp/orgajs/commit/bf0e6954246f7ed78a014601aca756cd47fa4fec)) ### Features - add jsx support ([0d22499](https://github.com/orgapp/orgajs/commit/0d224990b412e064ebf6816608eea6766f93d60c)) - better code block in website ([3efe4cd](https://github.com/orgapp/orgajs/commit/3efe4cd96a63623e2f70028bd66346960ec90bec)) - **gatsby:** process images ([76fdade](https://github.com/orgapp/orgajs/commit/76fdaded0d87a3bc2e188392b520f62ad789598c)) - **gatsby-plugin-orga:** better code block ([e6d7d20](https://github.com/orgapp/orgajs/commit/e6d7d20f63fa1871d8f53b0534b50ac6d7d99fc9)) ## [2.4.9](https://github.com/orgapp/orgajs/compare/v2.4.8...v2.4.9) (2021-07-13) **Note:** Version bump only for package orgajs ## [2.4.8](https://github.com/orgapp/orgajs/compare/v2.4.7...v2.4.8) (2021-04-26) ## 2.4.6 (2021-04-25) **Note:** Version bump only for package orgajs ## [2.4.7](https://github.com/orgapp/orgajs/compare/v2.4.6...v2.4.7) (2021-04-26) **Note:** Version bump only for package orgajs ================================================ FILE: CONTRIBUTING.org ================================================ #+title: How to contribute to orgajs Hi 👋 🦄. * Getting Started Make sure you have latest yarn installed locally. To get started with the repo, after cloning the repo. #+begin_src sh yarn install #+end_src * Ways to Contribute ** Improve documentation Documentations resides in the [[file:docs][docs]] folder. The content will be published to the [[https://orga.js.org][official website]]. As a user of the library and tools, you are the best person for writing them. Any help with documentation would be appreciated greatly. ** Write code and tests It's often a good idea to create an issue to discuss the bug or feature before creating a pull request to prevent from doing unnecessary work. ** Discussion Have some cool ideas? Let's talk about it in the [[https://github.com/orgapp/orgajs/discussions][Discussions]] tab. * Commit Messages We are following the [[https://www.conventionalcommits.org/en/v1.0.0/][conventional commits]] standards, an example would be: #+begin_example feat(parser): add new syntax this is a body #+end_example The above commit message would result in a minor version bump when it is merged into master for release. Take a look at the spec for more details. This is for extracting changelog and streamline version bump automatically. * Run Unit Tests #+begin_src sh yarn build # always build before running tests yarn test #+end_src * Submitting Pull Requests Please submit pull requests against =develop= branch. =master= is for releasing. * Releasing All packages in orgajs is automatically released once merged into master. That's why the commit message is extra important. Especially when there are =BRAKING CHANGE=. ================================================ FILE: LICENSE.org ================================================ The MIT License (MIT) Copyright (c) 2015 gatsbyjs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.org ================================================ #+title: Orga #+subtitle: org-mode < JavaScript * What Is It =Orga= is a flexible org-mode syntax parser. It parses org content into AST ([[https://en.wikipedia.org/wiki/Abstract_syntax_tree][Abstract Syntax Tree 🌲]]). And it's written in JavaScript. * Why org-mode is simply a superior format than other more popular ones, but it's mostly trapped inside of emacs. It's so good that it was the #1 reason to learn and use emacs for a lot of people (me included). But it's too good to not share with the rest of the world. If it can run in JavaScript, it can run on anything. * Compatible Eco-systems It integrates natively with popular tools. ** [[https://unifiedjs.com][Unified]] #+BEGIN_QUOTE ☔️ interface for parsing, inspecting, transforming, and serializing content through syntax trees #+END_QUOTE The =orga= parser is completely compatible with unified. Which means you get to take advantage of the works of others put into the pipeline. [[https://github.com/retextjs/retext][linting for natural language]], [[https://alexjs.com][correct your writing]], [[https://wooorm.com/write-music/][write music]]? etc. Here is [[https://github.com/orgapp/orgajs/tree/develop/examples/getting-started][an example]]. ** [[https://webpack.js.org][Webpack]] =@orgajs/loader= is a webpack loader that made orga native citizen of webpack ecosystem. Coupled with plugins, it works smoothly. Take a look at [[https://github.com/orgapp/orgajs/tree/develop/examples/webpack][the example project]]. ** [[https://reactjs.org/][React]] #+begin_quote A JavaScript library for building user interfaces #+end_quote You can render react components directly in your org file. Something like this: #+begin_src org ,* Hello World Let's render *the box*. ,#+begin_export jsx
I am a box with shadow
,#+end_export #+end_src [[https://orga.js.org/playground/?text=*%20Hello%20World%0A%0ALet's%20render%20*the%20box*.%0A%0A%23%2Bbegin_export%20jsx%0A%3Cdiv%20style%3D%7B%7B%0A%20%20backgroundColor%3A%20'gold'%2C%20%0A%20%20padding%3A%20'1em'%2C%0A%20%20border%3A%20'1px%20solid%20black'%2C%0A%20%20boxShadow%3A%20'5px%205px'%0A%7D%7D%3EI%20am%20a%20box%20with%20shadow%3C%2Fdiv%3E%0A%23%2Bend_export%0A][Try it our yourself in the playground]]. ** [[https://vitejs.dev][Vite]] #+begin_quote Next generation frontend tooling #+end_quote =@orgajs/rollup= is a plugin compatible with both Rollup and Vite. It lets you import =.org= files directly in your project. - Package: =@orgajs/rollup= ** [[https://astro.build][Astro]] #+begin_quote The web framework for content-driven websites #+end_quote Astro integration is maintained in a standalone repository. - [[https://github.com/orgapp/orga-astro][orga-astro repository]] - Package: =@orgajs/astro= ** [[https://nextjs.org][Nextjs]] #+begin_quote The React Framework #+end_quote Next.js integration is maintained in a standalone repository. - [[https://github.com/orgapp/orga-next][orga-next repository]] - Package: =@orgajs/next= - Guide: [[https://orga.js.org/guides/next][Next.js integration guide]] * Examples Take a look at the [[https://github.com/orgapp/orgajs/tree/main/examples][collection of examples]] to quickly get started. * Contribute See the [[file:CONTRIBUTING.org][contributing file]] for ways to get started. ================================================ FILE: biome.json ================================================ { "$schema": "https://biomejs.dev/schemas/2.4.4/schema.json", "files": { "includes": ["packages/**", "!packages/**/__tests__/**/*.json"] }, "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, "formatter": { "indentStyle": "tab", "bracketSpacing": true }, "javascript": { "formatter": { "quoteStyle": "single", "semicolons": "asNeeded", "trailingCommas": "none" } }, "linter": { "rules": { "complexity": { "useArrowFunction": "off" } } } } ================================================ FILE: docs/.gitignore ================================================ out ================================================ FILE: docs/_components.tsx ================================================ import { Editor } from '@orgajs/react-editor' import { ReactCodeMirror } from '@orgajs/react-cm' import { tags as t } from '@lezer/highlight' import { EditorView } from '@codemirror/view' import { HighlightStyle, defaultHighlightStyle, syntaxHighlighting, foldGutter } from '@codemirror/language' import { javascript } from '@codemirror/lang-javascript' export function Notice({ title, children }: { title?: string children: React.ReactNode }) { return (
{children}
) } const nord0 = '#2E3440', nord1 = '#3B4252', nord2 = '#434C5E', nord3 = '#4C566A', nord4 = '#D8DEE9', nord5 = '#E5E9F0', nord6 = '#ECEFF4', nord7 = '#8FBCBB', nord8 = '#88C0D0', nord9 = '#81A1C1', nord10 = '#5E81AC', nord11 = '#BF616A', nord12 = '#D08770', nord13 = '#EBCB8B', nord14 = '#A3BE8C', nord15 = '#B48EAD' const nordTheme = EditorView.theme( { '&': { color: nord4, backgroundColor: nord0 }, '.cm-content': { caretColor: nord4 }, '.cm-cursor, .cm-dropCursor': { borderLeftColor: nord4 }, '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': { backgroundColor: nord2 }, '.cm-panels': { backgroundColor: nord1, color: nord5 }, '.cm-panels.cm-panels-top': { borderBottom: `2px solid ${nord3}` }, '.cm-panels.cm-panels-bottom': { borderTop: `2px solid ${nord3}` }, '.cm-searchMatch': { backgroundColor: nord13, outline: `1px solid ${nord3}` }, '.cm-searchMatch.cm-searchMatch-selected': { backgroundColor: nord12 }, '.cm-activeLine': { backgroundColor: nord1 }, '.cm-activeLineGutter': { backgroundColor: nord2 }, '.cm-selectionMatch': { backgroundColor: nord3 }, '.cm-matchingBracket, .cm-nonmatchingBracket': { backgroundColor: nord8, outline: `none` }, '.cm-gutters': { backgroundColor: nord0, color: nord3, border: 'none' }, '.cm-lineNumbers .cm-gutterElement': { padding: '0 3px 0 5px' }, '.cm-tooltip': { border: `1px solid ${nord3}`, backgroundColor: nord2 }, '.cm-tooltip-autocomplete': { '& > ul > li[aria-selected]': { backgroundColor: nord3, color: nord6 } } }, { dark: true } ) const nordHighlightStyle = HighlightStyle.define([ { tag: t.keyword, color: nord9 }, { tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: nord4 }, { tag: [t.function(t.variableName), t.labelName], color: nord8 }, { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: nord7 }, { tag: [t.definition(t.name), t.separator], color: nord4 }, { tag: [t.className], color: nord7 }, { tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: nord15 }, { tag: [t.typeName], color: nord7 }, { tag: [t.operator, t.operatorKeyword], color: nord9 }, { tag: [t.url, t.escape, t.regexp, t.link], color: nord13 }, { tag: [t.meta, t.comment], color: nord3, fontStyle: 'italic' }, { tag: [t.strong], fontWeight: 'bold' }, { tag: [t.emphasis], fontStyle: 'italic' }, { tag: [t.strikethrough], textDecoration: 'line-through' }, { tag: [t.string], color: nord14 }, { tag: [t.invalid], color: nord11 } ]) const nord = [nordTheme, syntaxHighlighting(nordHighlightStyle)] export function JSEditor({ className, children }: { className?: string children: string }) { return (
) } export function OrgEditor({ className = '', content, onChange }) { return ( ) } ================================================ FILE: docs/_layout.tsx ================================================ import { ReactNode } from 'react' import { Link } from 'orga-build/components' import type { SVGProps } from 'react' const GitHub = (props: SVGProps) => ( ) interface LayoutProps { title: string children: ReactNode } const navItems = [ { name: 'Orga', href: '/' }, { name: 'Documents', href: '/guides' }, { name: 'Playground', href: '/playground' } ] export default function Layout({ children }: LayoutProps) { return ( <>
{children}
{/* Client-side JS is now added by build process */} ) } type Page = { slug: string title: string position: number type: string } function findParentSlug(slug: string, pages: Page[]): string | null { const parts = slug.split('/').filter(Boolean) while (parts.length > 0) { parts.pop() const parentSlug = '/' + parts.join('/') if (pages.some((p) => p.slug === parentSlug)) return parentSlug } return null } function renderMenu(path: string, pages: Page[]) { const children = pages .filter( (p) => findParentSlug(p.slug, pages) === path && p.type === 'document' ) .sort((a, b) => (Number(a.position) || 0) - (Number(b.position) || 0)) if (children.length === 0) return null return ( ) } export function DocumentLayout({ title, pages = [], children }) { return (
    {renderMenu('/', pages)}

{title}

{children}
) } function Content({ children }: { children: ReactNode }) { return (
{children}
) } ================================================ FILE: docs/_snippets/hey.org ================================================ * Hey, there The /beauty/ of org *must* be shared. [[https://upload.wikimedia.org/wikipedia/commons/a/a6/Org-mode-unicorn.svg][org-mode logo]] ================================================ FILE: docs/advanced/_layout.tsx ================================================ import { DocumentLayout } from '../_layout.tsx' export default DocumentLayout ================================================ FILE: docs/advanced/api.org ================================================ #+title: API #+published: true #+position: 201 #+type: document #+jsx: This page is a WIP Describe the API. ================================================ FILE: docs/advanced/ast.org ================================================ #+title: AST #+published: true #+position: 202 #+type: document #+jsx: This page is a WIP Orga Abstract Syntax Tree. ================================================ FILE: docs/advanced/index.org ================================================ #+title: Advanced #+published: true #+type: document #+position: 200 #+jsx: This page is a WIP ================================================ FILE: docs/advanced/latex.org ================================================ #+title: Latex Support #+published: true #+position: 203 #+type: document ** Inline Math Orgajs supports inline math with latex math delimiters. #+begin_src org If $$a^2=b$$ and \( b=2 \), then the solution must be either $$ a=+\sqrt{2} $$ or \[ a=-\sqrt{2} \]. #+end_src With be rendered as follows. If $$a^2=b$$ and \( b=2 \), then the solution must be either $$ a=+\sqrt{2} $$ or \[ a=-\sqrt{2} \]. ** Latex Block It also supports =\begin= commands. #+begin_src org \begin{equation} x=\sqrt{b} \end{equation} #+end_src Will be rendered \begin{equation} x=\sqrt{b} \end{equation} ** Styling =@orgajs/rehype-latex= is the plugin for handling latex. It uses [[https://katex.org][katex]] underneath, so you will have to add the css link yourself in your website. Add the following to the =head=. ================================================ FILE: docs/contribute.org ================================================ #+title: Contribute #+published: true #+position: 300 #+type: document #+jsx: This page is a WIP * Need Some Inspiration? You can find tons of plugins for remark, since they work in similar ways, we can steal some ideas [[https://github.com/remarkjs/remark/blob/main/doc/plugins.md#creating-plugins][here]] and [[https://github.com/remarkjs/awesome-remark][there]], maybe even use them directly. ================================================ FILE: docs/guides/_layout.tsx ================================================ import { DocumentLayout } from '../_layout.tsx' export default DocumentLayout ================================================ FILE: docs/guides/astro.org ================================================ #+title: Astro #+published: true #+type: document #+position: 5 Use =@orgajs/astro= to write Astro pages in Org Mode (=.org=) and compile them with orgx. * Quick Start 1. Install the integration: #+begin_src sh pnpm add @orgajs/astro #+end_src 2. Register it in =astro.config.mjs=: #+begin_src javascript import { defineConfig } from 'astro/config' import orgMode from '@orgajs/astro' export default defineConfig({ integrations: [orgMode()] }) #+end_src 3. Create a page at =src/pages/index.org=: #+begin_src org ,* Hello from Org Mode This page is rendered from Org Mode in Astro. #+end_src 4. Start Astro: #+begin_src sh pnpm astro dev #+end_src * Major Features ** Native =.org= pages in Astro Any file in =src/pages/*.org= is treated as a page route, just like Astro files in other supported formats. ** Org metadata support Org keywords such as =#+title:=, =#+description:=, and =#+slug:= are parsed and exposed as entry metadata for Astro content handling. ** orgx-powered compilation Under the hood, this integration uses orgx (via =@orgajs/rollup=), so you get: - Org Mode parsing to JSX-compatible output - JavaScript imports/exports inside Org documents - Unified/rehype/recma pipeline compatibility ** Inline JSX components You can embed JSX directly in Org files, including imported components. #+begin_src org ,#+jsx: import Card from '../components/Card.astro' Here is a Card rendered inline: ,#+jsx: Hello from Org + JSX ,#+begin_export jsx

JSX export block

You can also write larger JSX blocks.

,#+end_export #+end_src Use =#+jsx:= for single-line JSX/imports and =#+begin_export jsx= for multi-line blocks. ================================================ FILE: docs/guides/gatsby.org ================================================ #+title: Gatsby #+published: true #+type: document #+position: 102 #+jsx: I'm no longer actively maintaining this Gatsby integration due to the rapid pace of frontend development. However, the source code remains available for those who wish to adapt and maintain it independently. You can refer to the orgajs project for examples of advanced usage. * Create Gatsby Project Create a new gatsby website following the [[https://www.gatsbyjs.com/get-started/][documentation]]. Or simply #+begin_src sh npx gatsby new gatsby-site #+end_src * Installation Install packages. #+begin_src sh cd gatsby-site yarn add gatsby-plugin-orga @orgajs/react @orgajs/loader #+end_src * Configuration Add =gatsby-plugin-orga= to =gatsby-config.js=. #+begin_src javascript module.exports = { plugins: ['gatsby-plugin-orga'] } #+end_src Add a org file in folder =src/pages= directory. It should work out of the box. For more advanced usage, please checkout code of [[https://github.com/orgapp/orgajs][orgajs project]], which generate this website via =gatsby-theme-orga-docs=. ================================================ FILE: docs/guides/index.org ================================================ #+title: Getting Started #+published: true #+type: document #+position: 100 ** Basic Setup Orga is built on the [[https://unifiedjs.com][unified]] ecosystem. The core parser package =@orgajs/reorg= is the minimum requirement to get started. ** Simple HTML Compilation To transform Org-mode content into HTML, install the required packages: #+begin_src sh npm install @orgajs/reorg @orgajs/reorg-rehype rehype-stringify unified-stream #+end_src Create a basic compilation script: #+begin_src javascript // compile.js const stream = require('unified-stream') const reorg = require('@orgajs/reorg') const mutate = require('@orgajs/reorg-rehype') const html = require('rehype-stringify') const processor = reorg() .use(mutate) .use(html) process.stdin.pipe(stream(processor)).pipe(process.stdout) #+end_src Convert your Org files to HTML: #+begin_src sh node compile.js < input.org > output.html #+end_src #+end_src Example Input (input.org) #+begin_src org ,* Hello Orga Orga is *awesome*. #+end_src Yields Output (output.html) #+begin_src html

Hello Orga

Orga is awesome.

#+end_src ================================================ FILE: docs/guides/next.org ================================================ #+title: Next.js #+published: true #+type: document #+position: 4 Use =@orgajs/next= to write Next.js pages in Org Mode (=.org=) and compile them with orgx. Standalone repository: - [[https://github.com/orgapp/orga-next][orgapp/orga-next]] * Quick Start 1. Install the integration: #+begin_src sh pnpm add @orgajs/next @orgajs/loader @orgajs/react #+end_src 2. Register it in =next.config.js=: #+begin_src javascript const withOrg = require('@orgajs/next')() module.exports = withOrg({ pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'org'], }) #+end_src 3. Create a page at =app/page.org= (or =pages/index.org=): #+begin_src org ,* Hello from Org Mode This page is rendered from Org Mode in Next.js. #+end_src 4. Start Next.js: #+begin_src sh pnpm next dev #+end_src * Major Features ** Native =.org= routes in Next.js Org files can be used as top-level routes in Next.js once =pageExtensions= includes =org=. ** Works with App Router and Pages Router You can place Org pages in either: - =app/**/page.org= - =pages/**/*.org= ** Global Org component mapping Create =org-components.js= at project root to override rendered elements globally. #+begin_src javascript export function useOrgComponents() { return { h1: (props) =>

, } } #+end_src ** orgx-powered compilation This integration uses =@orgajs/loader= under the hood, providing Org Mode to JSX compilation and compatibility with orgx features. * Notes - Package name: =@orgajs/next= - Repository: [[https://github.com/orgapp/orga-next][orgapp/orga-next]] ================================================ FILE: docs/guides/orga-build.org ================================================ #+title: Build a blog with orga-build #+position: 2 #+type: document =orga-build= is a powerful static site generator that allows you to build entire websites using only Org-mode files. Tired of learning ever changing frontend frameworks, just want to build a good old static website? This is for you. Get a bunch of org files in a folder. Run #+begin_src sh npx orga-build #+end_src This command will generate a static website in the =out= folder, with each Org-mode file translated into an HTML file. The end. ** Layouts Layout is powerful tool that allows you to define the overall structure and appearance of your website pages. If you are not allergic to write a little bit of =jsx=, you can create layout files and nest them within folders to apply specific layouts to different sections of your website. To add a layout, create a file named =_layout.tsx= in the desired folder. The root-level layout file (=/_layout.tsx=) will be applied to all pages, while nested layout files (e.g., =/blog/_layout.tsx=) will be applied to all pages within that specific folder, nested inside the root-level layout. *** Layout Props Layout components have access to the in-buffer settings of the corresponding Org-mode file. For example, if you have the following lines in your Org-mode file: #+begin_src org ,#+title: My Page ,#+author: John #+end_src These values will be available as props in your layout file, allowing you to use them dynamically. Additionally, the layout component receives a =pages= prop, which is an array of page objects representing the pages within the current folder. This feature makes it easy to build navigation components that reflect the structure of your website. ** Build Commands =orga-build= supports two lifecycle hooks: =preBuild= and =postBuild=. These hooks allow you to run custom commands before and after the main build process, respectively. You can set up these hooks in the =orga.config.js= file by exporting named variables: #+begin_src javascript export const preBuild = ['npm run build:css'] #+end_src The =preBuild= and =postBuild= variables should be arrays of strings, where each string represents a shell command to be executed by the build process. ** Custom Components =orga-build= allows you to create and use custom React components within your Org-mode files. To do this, create a file named =_components.tsx= in your project root and export your custom components: #+begin_src jsx export function FancyBox({ children }) { return
{children}
; } #+end_src You can then use these components directly in your Org-mode files using the =#+jsx:= syntax: #+begin_src org #+jsx: hey, box #+end_src This powerful feature enables you to extend the functionality of your website and create rich, interactive experiences. ================================================ FILE: docs/guides/webpack.org ================================================ #+title: Webpack #+published: true #+type: document #+position: 3 =@orgajs/loader= is a webpack loader taht can be used natrually with webpack setup. For hassle free experience, use [[file:orga-build.org][orga-build]]. * Installation #+begin_src sh npm install --save-dev @orgajs/loader @orgajs/estree-jsx @orgajs/rehype-estree @orgajs/reorg-rehype #+end_src * Configuration An example =webpack.config.js= file. #+begin_src javascript import toEstree from '@orgajs/rehype-estree' import toRehype from '@orgajs/reorg-rehype' import toJsx from '@orgajs/estree-jsx' const config = { mode: 'development', module: { rules: [ { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, { test: /\.org$/, use: [ 'babel-loader', { loader: '@orgajs/loader', options: { plugins: [ toRehype, toEstree, toJsx, ] } }], }, ] }, } export default config #+end_src As you can see that the output of =@orgajs/loader= is normal JSX code, so you will need [[https://webpack.js.org/loaders/babel-loader/][babel-loader]] to finish it at the end. * Babel Configuration YOu will need to configure babel to support react syntax. An example of =.babelrc= file. #+begin_src json { "presets": ["@babel/env", "@babel/react"] } #+end_src Take a look at webpack documentation for creating a basic react project for more details. ================================================ FILE: docs/index.org ================================================ #+title: Orgajs #+jsx: import code from './index.org?raw' * What Is It =Orga= is a flexible org-mode syntax parser. It parses org content into AST ([[https://en.wikipedia.org/wiki/Abstract_syntax_tree][Abstract Syntax Tree 🌲]]). And it's written in JavaScript. * What can I do with it ** Editor Introducing org editor. This the source code of *this page*. #+jsx: ** Publication Build a website with org-mode files and [[/guides/orga-build][orga-build]]. # the "orga-editor" is a web-component, the following line defines it #+jsx: ================================================ FILE: examples/editor/package.json ================================================ { "name": "@orgajs/example-editor", "private": true, "version": "0.3.0", "type": "module", "scripts": { "dev": "vite --port 3000 --force", "build": "tsc && vite build", "preview": "vite preview" }, "devDependencies": { "@codemirror/view": "^6.36.2", "typescript": "^5.9.2", "vite": "^6.0.11" }, "dependencies": { "@orgajs/editor": "workspace:^", "cm6-theme-nord": "^0.2.0" } } ================================================ FILE: examples/editor/src/main.ts ================================================ import { makeEditor } from '@orgajs/editor' import content from '../content.org?raw' import './style.css' const target = document.querySelector('#editor') if (target === null) { throw new Error('No target element found') } makeEditor({ target, content, extensions: [] }) ================================================ FILE: examples/editor/src/style.css ================================================ body { background-color: #e7e6e5; } #editor { width: 720px; height: 800px; overflow: auto; border: 1px solid #ccc; background-color: #fff; } ================================================ FILE: examples/editor/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "skipLibCheck": true, /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src"] } ================================================ FILE: examples/getting-started/.gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Typescript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # next.js build output .next ================================================ FILE: examples/getting-started/CHANGELOG.md ================================================ # Change Log ## 4.2.12 ### Patch Changes - @orgajs/reorg-rehype@4.3.11 ## 4.2.11 ### Patch Changes - @orgajs/reorg-rehype@4.3.10 ## 4.2.10 ### Patch Changes - Updated dependencies [bd2365a] - @orgajs/reorg@4.3.3 - @orgajs/reorg-rehype@4.3.9 ## 4.2.9 ### Patch Changes - @orgajs/reorg-rehype@4.3.8 ## 4.2.8 ### Patch Changes - @orgajs/reorg-rehype@4.3.7 ## 4.2.7 ### Patch Changes - @orgajs/reorg-rehype@4.3.6 ## 4.2.6 ### Patch Changes - @orgajs/reorg-rehype@4.3.5 ## 4.2.5 ### Patch Changes - @orgajs/reorg-rehype@4.3.4 ## 4.2.4 ### Patch Changes - @orgajs/reorg@4.3.2 - @orgajs/reorg-rehype@4.3.3 ## 4.2.3 ### Patch Changes - @orgajs/reorg-rehype@4.3.2 - @orgajs/reorg@4.3.1 ## 4.2.2 ### Patch Changes - @orgajs/reorg-rehype@4.3.1 ## 4.2.1 ### Patch Changes - Updated dependencies [188d30f] - @orgajs/reorg-rehype@4.3.0 - @orgajs/reorg@4.3.0 ## 4.2.0 ### Minor Changes - d8861c2: update unified ecosystem ### Patch Changes - Updated dependencies [d8861c2] - @orgajs/reorg-rehype@4.2.0 - @orgajs/reorg@4.2.0 ## 4.1.3 ### Patch Changes - @orgajs/reorg-rehype@4.1.3 ## 4.1.2 ### Patch Changes - @orgajs/reorg-rehype@4.1.2 - @orgajs/reorg@4.1.2 ## 4.1.1 ### Patch Changes - @orgajs/reorg-rehype@4.1.1 - @orgajs/reorg@4.1.1 ## 4.1.0 ### Minor Changes - 4d8efbb7: Add increamental parsing ability for the editor. ### Patch Changes - Updated dependencies [4d8efbb7] - @orgajs/reorg-rehype@4.1.0 - @orgajs/reorg@4.1.0 ## 4.0.0 ### Major Changes - 176a3b5d: # Migrate most of the ecosystem to ESM We are excited to announce that we have migrated most of our ecosystem to ESM! This move was necessary as the unified ecosystem had already transitioned to ESM, leaving our orgajs system stuck on an older version if we wanted to stay on commonjs. We understand that this transition may come with some inevitable breaking changes, but we have done our best to make it as gentle as possible. In the past, ESM support in popular frameworks like webpack, gatsby, and nextjs was problematic, but the JS world has steadily moved forward, and we are now in a much better state. We have put in a lot of effort to bring this project up to speed, and we are happy to say that it's in a pretty good state now. We acknowledge that there are still some missing features that we will gradually add back over time. However, we feel that the changes are now in a great state to be released to the world. If you want to use the new versions, we recommend checking out the `examples` folder to get started. We understand that this upgrade path may not be compatible with older versions, and we apologize for any inconvenience this may cause. However, we encourage you to consider starting fresh, as the most important part of your site should always be your content (org-mode files). Thank you for your understanding, and we hope you enjoy the new and improved ecosystem! ### Patch Changes - Updated dependencies [176a3b5d] - @orgajs/reorg-rehype@4.0.0 - @orgajs/reorg@4.0.0 ## 3.1.8 ### Patch Changes - Updated dependencies [eeccc870] - @orgajs/reorg-rehype@3.0.10 - @orgajs/reorg@3.1.7 ## 3.1.7 ### Patch Changes - @orgajs/reorg-rehype@3.0.9 - @orgajs/reorg@3.1.6 ## 3.1.6 ### Patch Changes - 4bde5155: tidy up dependencies - @orgajs/reorg-rehype@3.0.8 - @orgajs/reorg@3.1.5 ## 3.1.5 ### Patch Changes - @orgajs/reorg-rehype@3.0.7 - @orgajs/reorg@3.1.4 ## 3.1.4 ### Patch Changes - @orgajs/reorg-rehype@3.0.6 - @orgajs/reorg@3.1.3 ## 3.1.3 ### Patch Changes - @orgajs/reorg-rehype@3.0.5 - @orgajs/reorg@3.1.2 ## 3.1.2 ### Patch Changes - @orgajs/reorg-rehype@3.0.4 - @orgajs/reorg@3.1.1 ## 3.1.1 ### Patch Changes - Updated dependencies [7f209ff5] - @orgajs/reorg-rehype@3.0.3 ## 3.1.0 ### Minor Changes - eeea0c54: introduce new token: empty line ### Patch Changes - Updated dependencies [eeea0c54] - @orgajs/reorg@3.1.0 - @orgajs/reorg-rehype@3.0.2 ## 3.0.1 ### Patch Changes - Updated dependencies [6ed76057] - Updated dependencies [759e6149] - @orgajs/reorg@3.0.1 - @orgajs/reorg-rehype@3.0.1 ## 3.0.0 ### Major Changes - 8b02d10: # Features - more powerful and flexible lexer and parser - webpack support - `jsx` support - better code block rendering - better image processing in gatsby - updated examples - tons of bug fixes - brand new `gatsby-plugin-orga` ### Patch Changes - Updated dependencies [8b02d10] - @orgajs/reorg@3.0.0 - @orgajs/reorg-rehype@3.0.0 All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. # [2.6.0](https://github.com/orgapp/orgajs/compare/v2.5.0...v2.6.0) (2021-08-28) **Note:** Version bump only for package @orgajs/getting-started # [2.5.0](https://github.com/orgapp/orgajs/compare/v2.4.9...v2.5.0) (2021-08-27) ### Bug Fixes - lock unified & @types/unist version ([dc72217](https://github.com/orgapp/orgajs/commit/dc72217f0cfcd778436d704021116c8479f8ee1e)) ## [2.4.9](https://github.com/orgapp/orgajs/compare/v2.4.8...v2.4.9) (2021-07-13) **Note:** Version bump only for package example ## [2.4.8](https://github.com/orgapp/orgajs/compare/v2.4.7...v2.4.8) (2021-04-26) **Note:** Version bump only for package example ## [2.4.7](https://github.com/orgapp/orgajs/compare/v2.4.6...v2.4.7) (2021-04-26) **Note:** Version bump only for package example ================================================ FILE: examples/getting-started/README.org ================================================ This is an example project, with [[file:index.js][10 lines of code]]. #+BEGIN_SRC sh npm install npm run build #+END_SRC Take a look at =readme.html= file. ================================================ FILE: examples/getting-started/index.js ================================================ import { reorg } from '@orgajs/reorg' import { stream } from 'unified-stream' import mutate from '@orgajs/reorg-rehype' import html from 'rehype-stringify' const processor = reorg().use(mutate).use(html) process.stdin.pipe(stream(processor)).pipe(process.stdout) ================================================ FILE: examples/getting-started/package.json ================================================ { "private": true, "name": "@orgajs/getting-started", "version": "4.2.12", "description": "", "main": "index.js", "type": "module", "scripts": { "build": "node index.js < README.org > readme.html" }, "author": "", "license": "ISC", "dependencies": { "@orgajs/reorg": "workspace:*", "@orgajs/reorg-rehype": "workspace:*", "rehype-stringify": "^10.0.1", "unified": "11.0.5", "unified-stream": "^3.0.0" } } ================================================ FILE: examples/webpack/.babelrc ================================================ { "presets": ["@babel/preset-env", "@babel/preset-react"] } ================================================ FILE: examples/webpack/CHANGELOG.md ================================================ # Change Log ## 3.2.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ## 3.1.1 ### Patch Changes - e3ef3a5: build website with orga-build ## 3.1.0 ### Minor Changes - 4d8efbb7: Add increamental parsing ability for the editor. ## 3.0.1 ### Patch Changes - 4bde5155: tidy up dependencies ## 3.0.0 ### Major Changes - 8b02d10: # Features - more powerful and flexible lexer and parser - webpack support - `jsx` support - better code block rendering - better image processing in gatsby - updated examples - tons of bug fixes - brand new `gatsby-plugin-orga` All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. # [2.6.0](https://github.com/orgapp/orgajs/compare/v2.5.0...v2.6.0) (2021-08-28) **Note:** Version bump only for package @orgajs/example-webpack # [2.5.0](https://github.com/orgapp/orgajs/compare/v2.4.9...v2.5.0) (2021-08-27) ### Bug Fixes - fix examples ([bdcd265](https://github.com/orgapp/orgajs/commit/bdcd2655502a73800e8915ba09fd78452dff503f)) ## [2.4.9](https://github.com/orgapp/orgajs/compare/v2.4.8...v2.4.9) (2021-07-13) **Note:** Version bump only for package @orgajs/example-webpack ## [2.4.8](https://github.com/orgapp/orgajs/compare/v2.4.7...v2.4.8) (2021-04-26) **Note:** Version bump only for package @orgajs/example-webpack ## [2.4.7](https://github.com/orgapp/orgajs/compare/v2.4.6...v2.4.7) (2021-04-26) **Note:** Version bump only for package @orgajs/example-webpack ================================================ FILE: examples/webpack/README.org ================================================ #+title: Webpack + Orga This is a minimal webpack website that recognizes org-mode files as pages. ----- * Running locally See the [[file:../README.org][setup instructions]] before using this project. #+begin_src shell yarn start #+end_src ================================================ FILE: examples/webpack/package.json ================================================ { "name": "@orgajs/example-webpack", "private": true, "version": "3.2.0", "author": "Xiaoxing Hu ", "scripts": { "build": "webpack", "webpack": "webpack", "start": "webpack serve" }, "devDependencies": { "@babel/core": "^7.22.11", "@babel/preset-env": "^7.22.14", "@babel/preset-react": "^7.22.5", "@orgajs/loader": "workspace:^", "babel-loader": "^9.1.3", "react": "^19.0.0", "react-dom": "^19.0.0", "webpack": "^5.104.1", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1" } } ================================================ FILE: examples/webpack/public/index.html ================================================
================================================ FILE: examples/webpack/src/box.js ================================================ import React from 'react' export default ({ children }) => (
{children}
) ================================================ FILE: examples/webpack/src/hello.org ================================================ #+jsx: import Box from './box' * Hello #+begin_export jsx export default ({ children }) =>

Orga + Webpack

{children}
#+end_export This is *org-mode* with /JSX/! ----- #+begin_export jsx the tomato box #+end_export ================================================ FILE: examples/webpack/src/index.js ================================================ import React from 'react' import { createRoot } from 'react-dom/client' import Hello from './hello.org' const root = createRoot(document.getElementById('root')) root.render() ================================================ FILE: examples/webpack/webpack.config.mjs ================================================ export default { mode: 'development', module: { rules: [ { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/, }, { test: /\.org$/, use: ['@orgajs/loader'], }, ], }, } ================================================ FILE: orga.config.js ================================================ import tailwindcss from '@tailwindcss/vite' import rehypePrettyCode from 'rehype-pretty-code' export const containerClass = 'prose p-4' export const styles = ['docs/style.css'] export const vitePlugins = [tailwindcss()] export const rehypePlugins = [[rehypePrettyCode, { theme: 'github-dark' }]] export const root = 'docs' export const exclude = ['config.d.ts'] ================================================ FILE: package.json ================================================ { "name": "orgajs", "private": true, "type": "module", "devDependencies": { "@biomejs/biome": "^2.4.4", "@changesets/cli": "^2.29.8", "@codemirror/lang-javascript": "^6.2.3", "@codemirror/language": "^6.10.8", "@codemirror/theme-one-dark": "^6.1.2", "@codemirror/view": "^6.36.2", "@lezer/highlight": "^1.2.1", "@orgajs/orgx": "workspace:^", "@orgajs/react-cm": "workspace:^", "@orgajs/react-editor": "workspace:^", "@tailwindcss/typography": "^0.5.9", "@tailwindcss/vite": "^4.1.4", "@types/node": "^25.3.2", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "daisyui": "^5.0.28", "orga-build": "workspace:^", "react": "^19.0.0", "react-dom": "^19.0.0", "rehype-pretty-code": "^0.14.1", "shiki": "^3.23.0", "tailwindcss": "^4.0.3", "type-coverage": "^2.29.7", "typescript": "^5.9.2", "unist-util-map": "4.0.0", "vfile": "^6.0.3" }, "scripts": { "build": "pnpm -r --filter './packages/*' --if-present run build && tsc --build --clean && tsc --build && type-coverage", "clean": "tsc --build --clean", "lint": "biome lint .", "lint:fix": "biome lint . --write", "test": "pnpm run check && pnpm -r --filter './packages/*' --if-present run test", "format": "biome format --write .", "check": "biome check .", "check:fix": "biome check . --write", "changeset": "changeset", "ci:version": "changeset version", "ci:publish": "pnpm build && changeset publish", "docs": "orga-build", "docs:dev": "orga-build dev" } } ================================================ FILE: packages/astro/README.org ================================================ * @orgajs/astro (Moved) The Astro integration package has moved to a dedicated repository: [[https://github.com/orgapp/orga-astro][github.com/orgapp/orga-astro]] Please use that repository for source code, issues, and releases. ================================================ FILE: packages/codemirror-lang/CHANGELOG.md ================================================ # @orgajs/cm-lang ## 1.3.0 ### Minor Changes - a53cfea: all about the editor This release improves the editor with new fold/shift/todo actions and settings, while also refactoring orga tokenization/parsing and lezer conversion to improve TODO handling, context hashing, and tree generation consistency. ### Patch Changes - Updated dependencies [a53cfea] - @orgajs/lezer@1.4.0 ## 1.2.1 ### Patch Changes - @orgajs/lezer@1.3.1 ## 1.2.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ### Patch Changes - Updated dependencies [188d30f] - @orgajs/lezer@1.3.0 ## 1.1.6 ### Patch Changes - e3ef3a5: build website with orga-build - Updated dependencies [e3ef3a5] - @orgajs/lezer@1.2.1 ## 1.1.5 ### Patch Changes - Updated dependencies [d8861c2] - @orgajs/lezer@1.2.0 ## 1.1.4 ### Patch Changes - 0f825de5: update editor dependencies - Updated dependencies [0f825de5] - @orgajs/lezer@1.1.4 ## 1.1.3 ### Patch Changes - @orgajs/lezer@1.1.3 ## 1.1.2 ### Patch Changes - Updated dependencies [ea032b35] - @orgajs/lezer@1.1.2 ## 1.1.1 ### Patch Changes - ac322714: implement editor - Updated dependencies [ac322714] - @orgajs/lezer@1.1.1 ## 1.1.0 ### Minor Changes - 4d8efbb7: Add increamental parsing ability for the editor. ### Patch Changes - Updated dependencies [4d8efbb7] - @orgajs/lezer@1.1.0 ================================================ FILE: packages/codemirror-lang/index.js ================================================ /** @typedef {import('@lezer/common').SyntaxNode} SyntaxNode */ import { defineLanguageFacet, foldNodeProp, foldService, Language, LanguageSupport, syntaxTree } from '@codemirror/language' import { parser as baseParser } from '@orgajs/lezer' const data = defineLanguageFacet({}) // --- folding --- /** * @param {SyntaxNode} node */ function getHeadlineLevel(node) { if (node.type.name !== 'headline') return const stars = node.getChild('stars') if (stars) { const level = stars.to - stars.from return level } } /** * @param {SyntaxNode} headerNode */ function findSectionEnd(headerNode) { const level = getHeadlineLevel(headerNode) if (level === undefined) throw new Error('Not a headline') let last = headerNode for (;;) { const next = last.nextSibling if (!next) { break } const l = getHeadlineLevel(next) if (l !== undefined && l <= level) { return next.from - 1 // Escape the newline. TODO: is this safe? } last = next } return last.to } const headerIndent = foldService.of((state, start, end) => { for ( let /** @type {SyntaxNode | null} */ node = syntaxTree(state).resolveInner( end, -1 ); node; node = node.parent ) { if (node.from < start) { break } if (!node.type.name.startsWith('headline')) { continue } const upto = findSectionEnd(node) if (upto > end) { return { from: end, to: upto } } } return null }) const parser = baseParser.configure({ props: [ foldNodeProp.add((type) => { if (type.name !== 'headline') { return undefined } return (tree, state) => ({ from: state.doc.lineAt(tree.from).to, to: state.doc.lineAt(tree.from).to }) }) ] }) /** * @param {any} parser */ function mkLang(parser) { return new Language(data, parser, [headerIndent], 'org') } export function org() { const lang = mkLang(parser) return new LanguageSupport(lang) } export { tags } from '@orgajs/lezer' ================================================ FILE: packages/codemirror-lang/package.json ================================================ { "name": "@orgajs/cm-lang", "version": "1.3.0", "description": "codemirror language: org", "type": "module", "files": [ "index.js", "index.d.ts", "index.d.ts.map" ], "scripts": {}, "keywords": [ "org", "org-mode", "orgajs", "orga", "codemirror" ], "author": "Xiaoxing Hu ", "license": "MIT", "repository": { "type": "git", "url": "https://github.com:orgapp/orgajs.git", "directory": "packages/cm-lang" }, "dependencies": { "@codemirror/language": "^6.10.8", "@orgajs/lezer": "workspace:^" }, "devDependencies": { "@codemirror/state": "^6.5.2", "@lezer/common": "^1.1.1" } } ================================================ FILE: packages/codemirror-lang/tsconfig.json ================================================ { "extends": "../../tsconfig.json" } ================================================ FILE: packages/editor/CHANGELOG.md ================================================ # @orgajs/editor ## 1.4.1 ### Patch Changes - bd2365a: fix types and linting ## 1.4.0 ### Minor Changes - a53cfea: all about the editor This release improves the editor with new fold/shift/todo actions and settings, while also refactoring orga tokenization/parsing and lezer conversion to improve TODO handling, context hashing, and tree generation consistency. ### Patch Changes - Updated dependencies [a53cfea] - @orgajs/cm-lang@1.3.0 ## 1.3.1 ### Patch Changes - 60ad38f: migrate orga-build to be based on vite - @orgajs/cm-lang@1.2.1 ## 1.3.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ### Patch Changes - Updated dependencies [188d30f] - @orgajs/cm-lang@1.2.0 ## 1.2.2 ### Patch Changes - 2f7b62d: fix package.json file ## 1.2.1 ### Patch Changes - e3ef3a5: build website with orga-build - Updated dependencies [e3ef3a5] - @orgajs/cm-lang@1.1.6 ## 1.2.0 ### Minor Changes - d8861c2: update unified ecosystem ### Patch Changes - @orgajs/cm-lang@1.1.5 ## 1.1.7 ### Patch Changes - 0f825de5: update editor dependencies - Updated dependencies [0f825de5] - @orgajs/cm-lang@1.1.4 ## 1.1.6 ### Patch Changes - e9564ff5: remove opinionated extensions from default editor ## 1.1.5 ### Patch Changes - 0ffa3415: cleanup theme ## 1.1.4 ### Patch Changes - dc3a9db2: cleanup editor ## 1.1.3 ### Patch Changes - 7cfff79a: headline elements (stars, todo keywords and priority) end after the whitespaces - @orgajs/cm-lang@1.1.3 ## 1.1.2 ### Patch Changes - ea032b35: bug fix, link generating button - @orgajs/cm-lang@1.1.2 ## 1.1.1 ### Patch Changes - ac322714: implement editor - Updated dependencies [ac322714] - @orgajs/cm-lang@1.1.1 ## 1.1.0 ### Minor Changes - 4d8efbb7: Add increamental parsing ability for the editor. ### Patch Changes - Updated dependencies [4d8efbb7] - @orgajs/cm-lang@1.1.0 ================================================ FILE: packages/editor/README.org ================================================ * TODO Quick Start * Customization ** TODO Write a Theme ** TODO key bindings ================================================ FILE: packages/editor/index.js ================================================ /** * @typedef {import('./lib/editor.js').Config} EditorConfig */ export { tags } from '@orgajs/cm-lang' export { makeEditor } from './lib/editor.js' export { settings } from './lib/settings.js' export { setup } from './lib/setup.js' ================================================ FILE: packages/editor/lib/actions/fold.js ================================================ /** * @file Actions to shift sections. * * @typedef {import('@codemirror/view').EditorView} EditorView * @typedef {import('@lezer/common').Tree} Tree * @typedef {import('@lezer/common').SyntaxNode} Node * @typedef {import('@codemirror/state').ChangeSpec} ChangeSpec */ import { foldAll, foldCode, foldState, unfoldAll, unfoldCode } from '@codemirror/language' import { EditorState } from '@codemirror/state' import { selectedLines } from './utils' /** * @param {EditorView} view */ export function toggleFold(view) { const { state } = view const lines = selectedLines(view) for (const line of lines) { const folded = findFold(state, line.from, line.to) if (folded) { unfoldCode(view) } else { foldCode(view) } } return true } /** * @param {EditorState} state * @param {number} from * @param {number} to */ function findFold(state, from, to) { /** @type {{from:number, to:number} | null} */ let found = null state.field(foldState, false)?.between(from, to, (from, to) => { if (!found || found.from > from) found = { from, to } }) return found } /** * @param {EditorView} view */ export function toggleFoldAll(view) { const state = view.state const folds = state.field(foldState, false) if (folds?.size) { unfoldAll(view) } else { foldAll(view) } return true } ================================================ FILE: packages/editor/lib/actions/shift.js ================================================ /** * @file Actions to shift sections. * * @typedef {import('@codemirror/view').EditorView} EditorView * @typedef {import('@lezer/common').Tree} Tree * @typedef {import('@lezer/common').SyntaxNode} Node * @typedef {import('@codemirror/state').ChangeSpec} ChangeSpec */ import { syntaxTree } from '@codemirror/language' /** * Shift the headline. * @param {number} delta - The number of spaces to shift. Positive values shift right, negative values shift left. * @param {boolean} [recursive=false] - Whether to shift subsections recursively. */ export function shift(delta, recursive = false) { /** * @param {EditorView} view */ return function (view) { const { state } = view const tree = syntaxTree(state) const pos = state.selection.main.head // the selection must be in a headline const headline = getHeadline(tree, pos) if (!headline) return false /** @type {ChangeSpec[]} */ const changes = [] let cancelled = false if (recursive === false) { const stars = headline.getChild('stars') if (!stars || stars.to - stars.from + delta < 1) { return true } const change = _shift(stars, delta) if (change) { changes.push(change) } } else { const cursor = headline.cursor() const change = _shift(headline, delta) if (change) { changes.push(change) } else { return true } while (cursor.nextSibling()) { if (cursor.type.name === 'headline') { const change = _shift(cursor.node, delta) if (change) { changes.push(change) } else { cancelled = true break } } } } if (!cancelled && changes.length > 0) { view.dispatch({ changes }) } return true } } /** * shift the stars * @param {Node|null|undefined} node * @param {number} delta * @returns {ChangeSpec | undefined} */ function _shift(node, delta) { if (!node) return undefined if (node.type.name === 'headline') return _shift(node.getChild('stars'), delta) if (node.type.name !== 'stars') return if (delta > 0) { return { from: node.from, insert: '*'.repeat(delta) } } if (delta < 0 && node.to - node.from + delta >= 1) { return { from: node.from, to: node.from - delta, insert: '' } } } /** * @param {Tree} tree * @param {number} pos */ function getHeadline(tree, pos) { /** @type {import('@lezer/common').NodeIterator | null} */ let iter = tree.resolveStack(pos) while (iter) { const name = iter.node.type.name if (name.startsWith('headline')) return iter.node iter = iter.next } return null } ================================================ FILE: packages/editor/lib/actions/todo.js ================================================ /** * @typedef {import('@codemirror/view').EditorView} EditorView */ import { syntaxTree } from '@codemirror/language' import { parseTodoKeywords } from 'orga/todo' import { settings } from '../settings' import { getTodo } from './utils' /** * @param {EditorView} view */ export function toggleTodo(view) { const { state } = view const pos = state.selection.main.head const tree = syntaxTree(state) const _todo = getTodo(tree, pos) if (!_todo) return false const content = state.sliceDoc(_todo.from, _todo.to) const _settings = state.field(settings) const t = parseTodoKeywords(_settings.todo) const next = t.next(content) const change = { from: _todo.from, to: _todo.to, ...(next !== undefined && { insert: next }) } view.dispatch({ changes: change }) console.log({ todo: _todo, content, next }) return true } ================================================ FILE: packages/editor/lib/actions/utils.js ================================================ /** * @typedef {import('@codemirror/view').EditorView} EditorView * @typedef {import('@lezer/common').Tree} Tree */ /** * @param {EditorView} view */ export function selectedLines(view) { /** @type {Array} */ const lines = [] for (const { head } of view.state.selection.ranges) { if (lines.some((l) => l.from <= head && l.to >= head)) continue lines.push(view.lineBlockAt(head)) } return lines } /** * @param {Tree} tree * @param {number} pos */ export function getTodo(tree, pos) { const headline = getNode(tree, pos, 'headline') return headline?.getChild('todo') } /** * @param {Tree} tree * @param {number} pos * @param {string} type */ export function getNode(tree, pos, type) { /** @type {import('@lezer/common').NodeIterator | null} */ let iter = tree.resolveStack(pos) while (iter) { const name = iter.node.type.name if (name === type) return iter.node iter = iter.next } return null } ================================================ FILE: packages/editor/lib/editor.js ================================================ /** * @callback OnChange * @param {EditorState} state */ /** * @typedef Config * @property {Element} target * @property {string} [content=''] * @property {import('@codemirror/state').Extension} [extensions=[]] * @property {boolean} [dark=false] * @property {OnChange} [onChange=() => {}] */ import { EditorState } from '@codemirror/state' import { EditorView } from '@codemirror/view' import { setup } from './setup.js' /** * @param {Config} config */ export function makeEditor(config) { const { target, content = '', extensions = [], onChange } = config const state = EditorState.create({ doc: content, extensions: [setup, extensions] }) const editor = new EditorView({ state, parent: target, dispatch: (tr) => { editor.update([tr]) tr.docChanged && onChange && onChange(editor.state) } }) return { editor } } ================================================ FILE: packages/editor/lib/extensions/cleanup.js ================================================ /** * @typedef Range * @property {number} from * @property {number} to * * @typedef {object} Options * @property {boolean} hideStars * @property {boolean} hideLinks */ import { syntaxTree } from '@codemirror/language' import { Decoration, ViewPlugin } from '@codemirror/view' import { parseTodoKeywords } from 'orga/todo' import { settings } from '../settings' /** * @param {Range} a * @param {Range} b */ function overlap(a, b) { return a.from <= b.to && a.to >= b.from } /** * @param {Options} options */ export function cleanup(options) { return ViewPlugin.define( (view) => { let _data = createDecorations(view, options) let _selection = view.state.selection.main return { get decorations() { return _data.decorations }, get selection() { return _selection }, update(update) { _selection = update.state.selection.main if (update.docChanged) { _data = createDecorations(update.view, options) } } } }, { decorations: (v) => { const { decorations, selection } = v return decorations.update({ filter: (_from, _to, deco) => { const revealRange = deco.spec.revealRange if (revealRange) { return !overlap(revealRange, selection) } return true } }) }, eventHandlers: { // cmd+click to open links mousedown(e, view) { const pos = view.posAtCoords(e) if (!pos) return if (!e.metaKey) return this.decorations.between(pos, pos, (_from, _to, deco) => { const { tagName, attributes } = deco.spec if (tagName === 'a' && attributes) { e.preventDefault() // TODO: is it possible to make it a real link? // I guess that'd be difficult because it's a fucking editor window.open(deco.spec.attributes.href, '_blank') return false } }) } } } ) } // TODO: this plugin is getting big, in terms of responsibility, break it down /** * @param {Range | null} reveal */ function hide(reveal) { return Decoration.replace({ revealRange: reveal }) } /** * @param {boolean} actionable */ function todo(actionable) { return Decoration.mark({ attributes: { class: 'cm-org-todo', 'data-actionable': actionable.toString() } }) } /** * @param {import('@codemirror/view').EditorView} view * @param {Options} options */ function createDecorations(view, options) { const _settings = view.state.field(settings) const t = parseTodoKeywords(_settings.todo) let decorations = Decoration.none /** @type {Range[]} */ const links = [] /** @type {Range | null} */ let headlineRange = null /** @type {Range | null} */ let linkRange = null syntaxTree(view.state).iterate({ enter(node) { if (node.name.startsWith('headline')) { headlineRange = { from: node.from, to: node.to } } if (node.name === 'link') { linkRange = { from: node.from, to: node.to } } if (node.name === 'url') { if (linkRange === null) return // get text of node const text = view.state.doc.sliceString(node.from, node.to) decorations = decorations.update({ add: [ Decoration.mark({ tagName: 'a', attributes: { class: 'cm-link', href: text.slice(1, -1) } }).range(linkRange.from, linkRange.to) ] }) } if (options.hideLinks && (node.name === 'url' || node.name === 'mark')) { decorations = decorations.update({ add: [hide(linkRange).range(node.from, node.to)] }) } if (options.hideStars && node.name === 'stars') { const revealRange = { from: headlineRange?.from, to: headlineRange?.to } decorations = decorations.update({ add: [ Decoration.replace({ revealRange }).range(node.from, node.to) ] }) } if (node.name === 'todo') { const content = view.state.doc.sliceString(node.from, node.to).trim() decorations = decorations.update({ add: [todo(t.actionable(content)).range(node.from, node.to)] }) } if (node.name === 'done') { decorations = decorations.update({ add: [todo(false).range(node.from, node.to)] }) } }, leave: (node) => { if (node.name.startsWith('headline')) headlineRange = null if (node.name === 'link') linkRange = null } }) return { decorations, links } } ================================================ FILE: packages/editor/lib/settings.js ================================================ /** * @typedef {import('@codemirror/state').EditorState} EditorState * * @typedef {Object} TodoKeywordSet * @property {string[]} actionables - The keywords that represent actionable states (e.g. "TODO", "NEXT"). * @property {string[]} done - The keywords that represent completed states (e.g. "DONE", "CANCELLED"). * * @typedef {Object} Settings * @property {string} todo - The set of todo keywords used in the document. */ import { syntaxTree } from '@codemirror/language' import { StateField } from '@codemirror/state' export const settings = StateField.define({ create(state) { return extractSettings(state) }, update(value, tr) { if (tr.docChanged) { return extractSettings(tr.state) } return value } }) /** * @param {EditorState} state * @return {Settings} */ function extractSettings(state) { const tree = syntaxTree(state) /** @type {Settings} */ const settings = { todo: 'TODO DONE' } tree.iterate({ enter(node) { if (node.name === 'keyword') { const content = state.doc.sliceString(node.from, node.to).trim() const m = content.match(/^#\+(\w+):(?:[ \t]+(.*))?$/y) if (!m) return true const [_, key, value] = m if (!key || !value) return true if (key.toLowerCase() === 'todo') { settings.todo = value.trim() return true } } return true } }) return settings } ================================================ FILE: packages/editor/lib/setup.js ================================================ import { defaultKeymap } from '@codemirror/commands' import { bracketMatching, foldGutter } from '@codemirror/language' import { EditorView, highlightActiveLine, keymap } from '@codemirror/view' import { org } from '@orgajs/cm-lang' import { toggleFold, toggleFoldAll } from './actions/fold.js' import { shift } from './actions/shift.js' import { toggleTodo } from './actions/todo' import { cleanup } from './extensions/cleanup.js' import { settings } from './settings' import theme from './theme.js' const keys = [ { key: 'Tab', run: toggleFold, shift: toggleFoldAll }, { key: 'Cmd-ArrowLeft', run: shift(-1), shift: shift(-1, true), preventDefault: true }, { key: 'Cmd-ArrowRight', run: shift(1), shift: shift(1, true), preventDefault: true }, { key: 'Cmd-x', run: toggleTodo, preventDefault: true } ] export const setup = (() => [ org(), settings, theme, keymap.of([...keys, ...defaultKeymap]), highlightActiveLine(), foldGutter({ openText: '▾', closedText: '▸' }), EditorView.lineWrapping, bracketMatching(), cleanup({ hideStars: false, hideLinks: true }) ])() ================================================ FILE: packages/editor/lib/theme.js ================================================ import { HighlightStyle, syntaxHighlighting } from '@codemirror/language' import { EditorView } from '@codemirror/view' import { tags as t } from '@orgajs/cm-lang' const theme = EditorView.baseTheme({ '&': { height: '100%' }, '.cm-link': { cursor: 'pointer' }, '.cm-org-todo': { color: 'white', backgroundColor: 'green', padding: '0 2px', borderRadius: '2px', cursor: 'pointer' }, '.cm-org-todo[data-actionable="true"]': { backgroundColor: 'red' } }) const baseStyle = HighlightStyle.define([ { tag: [t.heading], fontWeight: 'bold', textDecoration: 'underline' }, { tag: [t.keyword, t.strong], fontWeight: 'bold' }, { tag: t.emphasis, fontStyle: 'italic' }, { tag: t.monospace, borderRadius: '4px', padding: '1px 4px', fontFamily: "'JetBrains Mono', monospace" }, { tag: t.strikethrough, textDecoration: 'line-through' }, { tag: t.underline, textDecoration: 'underline' } ]) const lightColors = HighlightStyle.define( [ { tag: t.keyword, color: '#e45649' }, { tag: t.comment, color: '#9ca0a4' }, { tag: t.processingInstruction, color: '#9ca0a4' }, { tag: t.attributeName, color: '#9ca0a4' } ], { themeType: 'light' } ) const darkColors = HighlightStyle.define( [ { tag: t.keyword, color: 'green' }, { tag: t.comment, color: 'red' }, { tag: t.processingInstruction, color: 'gray' }, { tag: t.attributeName, color: 'gray' } ], { themeType: 'dark' } ) export default [ theme, syntaxHighlighting(lightColors), syntaxHighlighting(darkColors), syntaxHighlighting(baseStyle) ] ================================================ FILE: packages/editor/package.json ================================================ { "name": "@orgajs/editor", "version": "1.4.1", "type": "module", "files": [ "lib/", "index.js", "index.d.ts", "index.d.ts.map" ], "description": "react org-mode editor based on prose-mirror", "scripts": {}, "repository": { "type": "git", "url": "https://github.com/orgapp/orgajs.git", "directory": "packages/editor" }, "keywords": [ "org-mode", "CodeMirror", "editor" ], "author": "Xiaoxing Hu ", "license": "MIT", "dependencies": { "@codemirror/commands": "^6.8.0", "@codemirror/language": "^6.10.8", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.36.2", "@orgajs/cm-lang": "workspace:^" }, "devDependencies": { "@lezer/common": "^1.2.3", "orga": "workspace:^", "vfile": "^6.0.3" } } ================================================ FILE: packages/editor/tsconfig.json ================================================ { "extends": "../../tsconfig.json" } ================================================ FILE: packages/esbuild/CHANGELOG.md ================================================ # @orgajs/esbuild ## 1.1.5 ### Patch Changes - bd2365a: fix types and linting - Updated dependencies [bd2365a] - @orgajs/orgx@2.6.1 ## 1.1.4 ### Patch Changes - Updated dependencies [a53cfea] - @orgajs/orgx@2.6.0 ## 1.1.3 ### Patch Changes - @orgajs/orgx@2.5.2 ## 1.1.2 ### Patch Changes - @orgajs/orgx@2.5.1 ## 1.1.1 ### Patch Changes - 8451164: move orga-build to esbuild ## 1.1.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ### Patch Changes - Updated dependencies [188d30f] - @orgajs/orgx@2.5.0 ## 1.0.1 ### Patch Changes - Updated dependencies [e3ef3a5] - @orgajs/orgx@2.4.1 ## 1.0.0 ### Major Changes - 351f690: introduce @orgajs/node-loader, @orgajs/esbuild, @orgajs/build - @orgajs/node-loader : the nodejs loader for org-mode files - @orgajs/esbuild : esbuild plugin - @orgajs/build : static site generator, a.k.a orga-build ### Patch Changes - Updated dependencies [351f690] - @orgajs/orgx@2.4.0 ================================================ FILE: packages/esbuild/index.js ================================================ /** * @import { CompileOptions } from '@orgajs/orgx' * @import { OnLoadResult, PluginBuild * } from 'esbuild' */ import fs from 'node:fs/promises' import { compile } from '@orgajs/orgx' import { SourceMapGenerator } from 'source-map' import { VFile } from 'vfile' const name = '@orgajs/esbuild' /** * Create Node.js hooks to handle org files. * * @param {Readonly | null | undefined} [options] * Configuration (optional). * @returns * Node.js hooks. */ function esbuild(options) { return { name, setup } /** * @param {PluginBuild} build * Build. * @returns {undefined} * Nothing. */ function setup(build) { build.onLoad({ filter: /\.org$/ }, onload) } /** * @param {any} data * Data. * @returns {Promise} * Result. */ async function onload(data) { const document = String( data.pluginData && data.pluginData.contents !== null && data.pluginData.contents !== undefined ? data.pluginData.contents : await fs.readFile(data.path) ) const file = new VFile({ path: data.path, value: document }) const code = await compile(file, { ...options, SourceMapGenerator }) const contents = String(code) + '\n' + '//# sourceMappingURL=data:application/json;base64,' + Buffer.from(JSON.stringify(file.map)).toString('base64') + '\n' return { contents } } } export default esbuild ================================================ FILE: packages/esbuild/package.json ================================================ { "name": "@orgajs/esbuild", "version": "1.1.5", "description": "esbuild plugin for orgajs", "type": "module", "exports": "./index.js", "files": [ "lib/", "index.d.ts.map", "index.d.ts", "index.js" ], "scripts": {}, "license": "MIT", "keywords": [ "esbuild", "jsx", "org-mode", "react" ], "author": "Xiaoxing Hu ", "repository": { "type": "git", "url": "https://github.com/orgapp/orgajs.git", "directory": "packages/esbuild" }, "dependencies": { "@orgajs/orgx": "workspace:^", "source-map": "^0.7.4", "vfile": "^6.0.3" }, "devDependencies": { "esbuild": "^0.24.2" } } ================================================ FILE: packages/lezer/CHANGELOG.md ================================================ # @orgajs/lezer ## 1.4.1 ### Patch Changes - bd2365a: fix types and linting - Updated dependencies [bd2365a] - orga@4.7.1 ## 1.4.0 ### Minor Changes - a53cfea: all about the editor This release improves the editor with new fold/shift/todo actions and settings, while also refactoring orga tokenization/parsing and lezer conversion to improve TODO handling, context hashing, and tree generation consistency. ### Patch Changes - Updated dependencies [a53cfea] - orga@4.6.0 ## 1.3.1 ### Patch Changes - Updated dependencies [60ad38f] - orga@4.5.1 ## 1.3.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ### Patch Changes - Updated dependencies [188d30f] - orga@4.5.0 ## 1.2.1 ### Patch Changes - e3ef3a5: build website with orga-build ## 1.2.0 ### Minor Changes - d8861c2: update unified ecosystem ### Patch Changes - Updated dependencies [d8861c2] - orga@4.4.0 ## 1.1.4 ### Patch Changes - 0f825de5: update editor dependencies ## 1.1.3 ### Patch Changes - Updated dependencies [7cfff79a] - orga@4.3.0 ## 1.1.2 ### Patch Changes - ea032b35: bug fix, link generating button ## 1.1.1 ### Patch Changes - ac322714: implement editor - Updated dependencies [ac322714] - orga@4.2.0 ## 1.1.0 ### Minor Changes - 4d8efbb7: Add increamental parsing ability for the editor. ### Patch Changes - Updated dependencies [4d8efbb7] - orga@4.1.0 ================================================ FILE: packages/lezer/README.org ================================================ #+title: Lezer Parser for org-mode * Tasks ** TODO properly support incremental parsing ** TODO finish nodes design ** TODO finish handlers ** TODO cleanup dependencies ================================================ FILE: packages/lezer/index.js ================================================ export { OrgParser, parser } from './lib/index.js' export { tags } from './lib/nodes.js' ================================================ FILE: packages/lezer/lib/context.js ================================================ /** * @typedef {import('@lezer/common').PartialParse} PartialParse * @typedef {import('@lezer/common').Input} Input * @typedef {import('@lezer/common').TreeFragment} TreeFragment * @typedef {import('./fragments.js').FragmentCursor} FragmentCursor */ import { getSettings, makeParser } from 'orga' import { fragmentCursor } from './fragments.js' import { nodes } from './nodes.js' import { toLezer } from './oast-to-lezer.js' import { treeBuilder } from './tree.js' /** * @param {import('./index.js').OrgParser} config * @param {Input} input * @param {TreeFragment[]} _fragments * @param {{from: number, to: number}[]} ranges * @returns {PartialParse} */ export function parseContext(config, input, _fragments, ranges) { const { log, nodeSet } = config let cursor = ranges[0].from const end = ranges[ranges.length - 1].to const rangeI = 0 /** @type {number | null} */ let stoppedAt = null const full = input.read(0, input.length) const builder = treeBuilder(nodeSet, nodes.document, 0, cursor, 0, 0) const fragments = _fragments.length ? fragmentCursor(config, _fragments, input) : null // Read settings directly from document text const settings = getSettings(full) /** @type {import('orga').Parser | null} */ let parser = null /** * check if we reached the end of the input (ranges) * finish the block in parser if exist * check if we can reuse a fragment * @returns {import('@lezer/common').Tree | null} */ function advance() { log('advance called', cursor, end) // TODO: should we set an end to the tree? // will take a look when this actually happens if (stoppedAt != null && cursor > stoppedAt) return builder.build() if (cursor >= end) { // end of the tree has to match the end of the input // sometimes the the chidren does not fill the whole tree // which causes the end of the tree to be smaller than the end of the input // set it to the end of the input will prevent unnecessary parsing return builder.build(end) } if (fragments !== null && couldReuse(fragments)) { const result = fragments.takeNodes(cursor, ranges, rangeI) if (result.taken > 0) { wrapUpParser() builder.addChildren(result.nodes, result.positions) cursor += result.taken return null } else { log('took nothing') } } if (!parser) { log('-- creating parser --', cursor, end) parser = makeParser(full, { range: { start: cursor, end }, settings, flat: true }) } // parser.advance should finish a block const document = parser.advance() if (typeof document === 'number') { cursor = document return null } wrapUpParser() return null } function wrapUpParser() { if (!parser) return const document = parser.finish() log( `wrap up parser: ${document.children.length}, ${document.position?.start.offset}, ${document.position?.end.offset}` ) const tree = toLezer(document, nodeSet) log(document) log(tree) builder.takeChildren(tree, document.position?.start.offset) builder.takeProps(tree) if (document.position?.end.offset) cursor = document.position.end.offset log('-- destroying parser --') parser = null } /** * check if we can reuse nodes from fragments * @param {FragmentCursor} fragments */ function couldReuse(fragments) { const hash = builder.hash // TODO: pass the real lineStart if (!fragments.moveTo(cursor, cursor)) return false if (!fragments.matches(hash)) return false return true } return { get parsedPos() { log('parsedPos called', cursor) // if return undefined, the call of advance was not as intense return cursor // return parser.now }, advance, stopAt(pos) { log('stopAt called', pos, stoppedAt) if (stoppedAt != null && stoppedAt < pos) throw new RangeError("Can't move stoppedAt forward") stoppedAt = pos }, get stoppedAt() { return stoppedAt } } } ================================================ FILE: packages/lezer/lib/fragments.js ================================================ /** * @typedef {import('@lezer/common').PartialParse} PartialParse * @typedef {import('@lezer/common').Input} Input * @typedef {import('@lezer/common').TreeFragment} TreeFragment * @typedef {import('@lezer/common').TreeCursor} TreeCursor * * @typedef TakeNodesResult * @property {Tree[]} nodes * @property {number[]} positions * @property {number} taken * * @typedef FragmentCursor * @property {(pos: number, lineStart: number) => boolean} moveTo * @property {(hash: number) => boolean} matches * @property {(start: number, ranges: {from: number, to: number}[], rangeI: number) => TakeNodesResult} takeNodes */ import { NodeProp, Tree } from '@lezer/common' import { nodes } from './nodes.js' /** * @param {import('./index.js').OrgParser} config * @param {TreeFragment[]} fragments * @param {Input} input * @returns {FragmentCursor} */ export function fragmentCursor({ log, nodeSet }, fragments, input) { let i = 0 /** @type {TreeCursor | null} */ let cursor = null /** @type {TreeFragment | null} */ let fragment = fragments.length ? fragments[i++] : null let fragmentEnd = -1 return { moveTo, matches, takeNodes } function nextFragment() { fragment = i < fragments.length ? fragments[i++] : null cursor = null fragmentEnd = -1 } /** * move the cursor to the first block after `pos`. * @param {number} pos * @param {number} lineStart */ function moveTo(pos, lineStart) { while (fragment && fragment.to < pos) nextFragment() if (!fragment || fragment.from > (pos ? pos - 1 : 0)) { return false } fragmentEnd = fragment.to if (fragmentEnd < 0) { let end = fragment.to while (end > 0 && input.read(end - 1, end) !== '\n') end-- fragmentEnd = end ? end - 1 : 0 } const c = cursor || fragment.tree.cursor() if (!cursor) { cursor = c c.firstChild() } // if (!c) { // c = cursor = fragment.tree.cursor() // c?.firstChild() // } const rPos = pos + fragment.offset while (c.to <= rPos) if (!c.parent()) return false for (;;) { if (c.from >= rPos) return fragment.from <= lineStart if (!c.childAfter(rPos)) return false } } /** * @param {number} hash */ function matches(hash) { const tree = cursor?.tree const result = tree ? tree.prop(NodeProp.contextHash) === hash : false if (result) { log('✅ fragment matches, reusing', { hash, np: tree?.prop(NodeProp.contextHash) }) } else { log('❌ fragment does not match, not reusing', { hash, np: tree?.prop(NodeProp.contextHash) }) } return result } /** * @param {number} start * @param {{from: number, to: number}[]} ranges * @param {number} rangeI * @returns {TakeNodesResult} */ function takeNodes(start, ranges, rangeI) { /** @type {TakeNodesResult} */ const result = { nodes: [], positions: [], taken: 0 } log('takeNodes', start, ranges, rangeI) if (!cursor || !fragment) return result const cur = cursor const off = fragment.offset let end = start // let prevEnd = end let blockI = 0 // let prevI = 0 const fragEnd = fragmentEnd - (fragment.openEnd ? 1 : 0) for (;;) { if (cur.to - off > fragEnd) { if (cur.type.isAnonymous && cur.firstChild()) continue log( `stop takeNodes from ${cur.name}: fe: ${fragEnd} | ${fragmentEnd}, cur: ${cur.name}, cur.from: ${cur.from}, cur.to: ${cur.to}, off: ${off}` ) break } const pos = toRelative(cur.from - off, ranges) if (cur.to - off <= ranges[rangeI].to) { // Fits in current range log( `Fits in current range, name: ${cur.name}, pos: ${pos}, cur.from: ${cur.from}, cur.to: ${cur.to}` ) const tree = cur.tree if (tree) { result.nodes.push(tree) result.positions.push(pos) } } else { log('create dummy') const dummy = new Tree( nodeSet.types[nodes.paragraph], [], [], 0 // cx.block.hashProp ) result.nodes.push(dummy) result.positions.push(pos) // reuse dummy? } if (cur.type.is('Block')) { log('got block:', cur.type.name) end = cur.to - off blockI = result.nodes.length } if (!cur.nextSibling()) break } log(`>> popping: ${blockI} / ${result.nodes.length}`) log('node:', result.nodes.map((n) => n.type.name).join(',')) while (result.nodes.length > blockI) { const n = result.nodes.pop() log('.. pop non blocks', n?.type.name) result.positions.pop() } result.taken = end - start return result } } /** * Convert an input-stream-relative position to a * Markdown-doc-relative position by subtracting the size of all input * gaps before `abs`. * @param {number} abs * @param {{from: number, to: number}[]} ranges */ function toRelative(abs, ranges) { let pos = abs for (let i = 1; i < ranges.length; i++) { const gapFrom = ranges[i - 1].to, gapTo = ranges[i].from if (gapFrom < abs) pos -= gapTo - gapFrom } return pos } ================================================ FILE: packages/lezer/lib/handlers.js ================================================ /** * @typedef {import('./types.js').Seed} Seed * @typedef {import('./types.js').Handler} Handler * @typedef {Record} Handlers * Handle nodes. */ import { NodeProp } from '@lezer/common' import { nodes } from './nodes.js' // class NodeProp extends _NodeProp { // static path = new _NodeProp({ perNode: true }) // } /** @type {NodeProp<{level: number}>} */ export const headlineProp = new NodeProp({ perNode: true }) export const documentProp = new NodeProp({ perNode: true // deserialize: (str) => { // console.log('deserialize', str) // return str // } }) /** * @type {Handlers} */ export const handlers = { document: (_s, doc) => { if (doc.type !== 'document') { return false } return { id: nodes.document, props: [[documentProp, doc.properties]] } }, headline: (_s, node) => { if (node.type === 'headline') { return { id: nodes.headline, props: [[headlineProp, { level: node.level }]] } } return false }, stars: () => nodes.stars, todo: (_s, node) => { if (node.type !== 'todo') { return false } return nodes.todo }, 'link.path': () => nodes.url, // 'link.description': () => nodes.linkDescription, link: () => nodes.link, opening: () => nodes.mark, closing: () => nodes.mark, block: () => nodes.block, html: () => nodes.html, jsx: () => nodes.jsx, 'block.begin': () => nodes.blockBegin, 'block.end': () => nodes.blockEnd, tags: () => nodes.marker, keyword: () => nodes.keyword, paragraph: () => nodes.paragraph, list: () => nodes.list, 'list.item.bullet': () => nodes.marker, text: (_, node) => { if (node.type !== 'text') { return false } switch (node.style) { case 'bold': return nodes.bold case 'italic': return nodes.italic case 'strikeThrough': return nodes.strikeThrough case 'code': return nodes.code case 'verbatim': return nodes.code case 'underline': return nodes.underline } return false } } ================================================ FILE: packages/lezer/lib/index.js ================================================ /** * @typedef {import('@lezer/common').PartialParse} PartialParse * @typedef {import('@lezer/common').Input} Input * @typedef {import('@lezer/common').TreeFragment} TreeFragment */ /** * @typedef OrgParserConfig * @property {import('@lezer/common').NodePropSource[] | undefined | null} props */ import { NodeSet, Parser } from '@lezer/common' import { parseContext } from './context.js' import { nodeSet } from './nodes.js' export { tags } from './nodes.js' export class OrgParser extends Parser { /** * @param {NodeSet} nodeSet * @param {(...data: any[]) => void} [log] */ constructor(nodeSet, log = () => {}) { super() this.nodeSet = nodeSet this.log = log } /** * @param {Input} input * @param {TreeFragment[]} fragments * @param {{from: number, to: number}[]} ranges * @returns {PartialParse} */ createParse(input, fragments, ranges) { const r = ranges.map((r) => `${r.from}-${r.to}`).join(', ') const frags = fragments .map( (f) => `(${f.from}-${f.to}, offset: ${f.offset}, openEnd: ${f.openEnd})` ) .join(' ') // TODO: add info more about fragments, how do I use ranges vs fragments? this.log(`createParse`, `ranges: (${r}), frags: [${frags}]`) const parse = parseContext(this, input, fragments, ranges) return parse } /** * @param {OrgParserConfig} config */ configure(config) { let { nodeSet } = this const nodeTypes = nodeSet.types.slice() nodeSet = new NodeSet(nodeTypes) if (config.props && config.props.length > 0) { nodeSet = nodeSet.extend(...config.props) } return new OrgParser(nodeSet, this.log) } } export const parser = new OrgParser( nodeSet, console.log.bind(console, 'orga-parser') ) ================================================ FILE: packages/lezer/lib/nodes.js ================================================ import { NodeProp, NodeSet, NodeType } from '@lezer/common' import { styleTags, Tag, tags as t } from '@lezer/highlight' let i = 0 export const nodes = Object.freeze({ none: i++, // block document: i++, headline: i++, paragraph: i++, keyword: i++, block: i++, list: i++, html: i++, jsx: i++, // inline stars: i++, todo: i++, done: i++, link: i++, marker: i++, bold: i++, italic: i++, code: i++, strikeThrough: i++, underline: i++, url: i++, blockBegin: i++, blockEnd: i++, // smaller mark: i++ }) /** * @type {NodeProp<{level: number}>} */ export const headlineProp = new NodeProp() /** @type {NodeType[]} */ export const nodeTypes = Object.entries(nodes).map(([name, id]) => NodeType.define({ id, name, props: id >= nodes.stars ? [] : [[NodeProp.group, ['Block']]], top: name === 'document' }) ) // extra tags const underline = Tag.define(t.content) const orgHighlighting = styleTags({ 'headline/...': t.heading, keyword: t.attributeName, link: t.link, 'stars mark blockBegin blockEnd': t.processingInstruction, italic: t.emphasis, bold: t.strong, strikeThrough: t.strikethrough, paragraph: t.content, list: t.list, underline: underline, 'html jsx code block': t.monospace }) export const tags = { ...t, underline } export const nodeSet = new NodeSet(nodeTypes).extend(orgHighlighting) ================================================ FILE: packages/lezer/lib/oast-to-lezer.js ================================================ /** * @typedef {import('orga').Document} OrgTree * @typedef {import('@lezer/common').Tree} LezerTree * @typedef {import('@lezer/common').NodeSet} NodeSet * @typedef {import('orga').Document} OastRoot * @typedef {import('orga').Content} OastContent * @typedef {import('orga').Parent} OastParent * @callback Mapping * Transform an oast node to prose node. * @param {OastNodes} node * @param {number} id * @param {Array | undefined} [children] * @returns {LezerTree | null | undefined} * @typedef {import('./types.js').State} ParseState */ import { NodeProp, Tree } from '@lezer/common' import { handlers } from './handlers.js' /** * @param {import('vfile').VFile | null} file * @param {NodeSet} nodeSet * @returns {ParseState} */ function createParseState(file, nodeSet) { /** @type {ParseState} */ const state = { file, ignore: ['newline', 'emptyLine'], nodeSet: nodeSet, handlers, one(node, parent, base = 0) { return one(this, node, parent, base) }, all(parent, base) { return all(this, parent, base) } } return state } /** * @param {OrgTree} tree * @param {NodeSet} nodeSet * @param {import('vfile').VFile | null} [file=null] * @returns {import('@lezer/common').Tree} */ export function toLezer(tree, nodeSet, file = null) { // TODO: inject gaps const state = createParseState(file, nodeSet) const result = state.one(tree) if (!result) { throw new Error('no result') } const t = result.nodes[0] const _props = tree.properties return t } /** @type {import('./types.js').Handler} */ function defaultUnknownHandler() { // console.log('unknown node', n.type, n) return true } /** * @param {import('./types.js').OastNode} node * @returns {[number, number]} */ function getRange(node) { const start = node.position?.start.offset || 0 const end = node.position?.end.offset || 0 return [start, end - start] } /** * @param {ParseState} state * @param {import('./types.js').OastNode} node * @param {OastParent | undefined} [parent] * @param {number} [base=0] * @returns {{nodes: LezerTree[], positions: number[]} | null | undefined}} */ function one(state, node, parent, base = 0) { if (state.ignore.includes(node.type)) { return state.all(node, base) } const handler = state.handlers[node.type] || defaultUnknownHandler const seed = handler(state, node, parent) if (typeof seed === 'boolean') { if (seed) { return state.all(node, base) } return null } let [id, skip, props] = typeof seed === 'number' ? [seed, false] : [seed.id, seed.skip, seed.props] const [loc, len] = getRange(node) const pos = loc - base /** @type {Tree[]} */ let nodes = [] /** @type {number[]} */ let positions = [] if (!skip) { const result = state.all(node, loc) nodes = result.nodes positions = result.positions } if (node.data?.hash !== undefined) { if (!props) { props = [] } props.push([NodeProp.contextHash, node.data.hash]) } const tree = new Tree(state.nodeSet.types[id], nodes, positions, len, props) const result = { nodes: [tree], positions: [pos] } return result } /** * @param {ParseState} state * @param {import('./types.js').OastNode} parent * @param {number} base * @returns {{nodes: LezerTree[], positions: number[]}}} */ function all(state, parent, base) { /** @type {Array} */ const _nodes = [] /** @type {Array} */ const _positions = [] if ('children' in parent) { let index = -1 while (++index < parent.children.length) { const node = parent.children[index] const { nodes, positions } = state.one(node, parent, base) || { nodes: [], positions: [] } _nodes.push(...nodes) _positions.push(...positions) } } return { nodes: _nodes, positions: _positions } } ================================================ FILE: packages/lezer/lib/tree.js ================================================ import { NodeProp, NodeType, Tree } from '@lezer/common' import { documentProp } from './handlers.js' // import { nodes } from './nodes.js' /** * @param {number} type * @param {number} value * @param {number} [parentHash=0] */ function hash(type, value, parentHash = 0) { return (parentHash + (parentHash << 8) + type + (value << 4)) | 0 } /** * @param {import('@lezer/common').NodeSet} nodeSet * @param {number} type * @param {number} value * @param {number} from * @param {number} parentHash * @param {number} originalEnd */ export function treeBuilder( nodeSet, type, value, from, parentHash, originalEnd ) { /** @type {Tree[]} */ const children = [] /** @type {number[]} */ const positions = [] const _hash = hash(type, value, parentHash) /** @type {[NodeProp, any][]} */ const props = [[NodeProp.contextHash, _hash]] return { addChild, addChildren, takeChildren, takeProps, addProps: (/** @type {[NodeProp, any][]} */ p) => props.push(...p), build, hash: _hash } /** * @param {Tree} child * @param {number} pos */ function addChild(child, pos) { if (child.prop(NodeProp.contextHash) !== _hash) child = new Tree( child.type, child.children, child.positions, child.length, props ) children.push(child) positions.push(pos) } /** * @param {Tree[]} children * @param {number[]} positions */ function addChildren(children, positions) { for (let i = 0; i < children.length; i++) addChild(children[i], positions[i]) } /** * @param {Tree} tree * @param {number} [offset=0] */ function takeChildren(tree, offset = 0) { // addChildren(tree.children, tree.positions) const cursor = tree.cursor() if (!cursor.firstChild()) return do { const child = cursor.tree if (!child) continue // TODO: is the position correct? addChild(child, cursor.from + offset) } while (cursor.nextSibling()) } /** * @param {Tree} tree */ function takeProps(tree) { const doc = tree.prop(documentProp) if (doc) { props.push([documentProp, doc]) } } /** * @param {number} [end = originalEnd] - end of the tree * @returns {Tree} */ function build(end = originalEnd) { const last = children.length - 1 if (last >= 0) end = Math.max(end, positions[last] + children[last].length + from) const tree = new Tree( nodeSet.types[type], children, positions, end - from ).balance({ makeTree: (children, positions, length) => new Tree(NodeType.none, children, positions, length, props) }) return tree } } ================================================ FILE: packages/lezer/lib/types.ts ================================================ import type { Tree as LezerTree, NodeSet } from '@lezer/common' import type { Content, Document, Parent as OastParent, Token } from 'orga' import type { Position } from 'unist' import type { VFile } from 'vfile' export type OastNode = Document | Content | Token type Action = boolean export type Seed = | { id: number position?: Position props?: any skip?: Action } | Action | number interface LezerChild { node: LezerTree position: number } export type Handler = ( state: State, node: OastNode, parent: OastParent | undefined ) => Seed export interface State { file: VFile | null ignore: string[] readonly nodeSet: NodeSet handlers: Record one: ( node: OastNode, parent?: OastParent | undefined, base?: number ) => { nodes: LezerTree[]; positions: number[] } | null | undefined all: ( node: OastNode, base: number ) => { nodes: LezerTree[]; positions: number[] } } ================================================ FILE: packages/lezer/package.json ================================================ { "name": "@orgajs/lezer", "version": "1.4.1", "description": "lezer parser for org-mode", "type": "module", "files": [ "lib/", "index.js", "index.d.ts", "index.d.ts.map" ], "scripts": { "test": "node --test tests/*.test.js" }, "keywords": [ "org-mode", "org", "parser", "lezer" ], "author": "Xiaoxing Hu ", "license": "MIT", "repository": { "url": "orgapp/orgajs", "directory": "packages/lezer" }, "dependencies": { "@lezer/common": "^1.1.1", "@lezer/highlight": "^1.2.1", "orga": "workspace:^", "unist-util-visit": "^5.0.0" }, "devDependencies": { "@types/unist": "^3.0.3", "text-kit": "workspace:^", "vfile": "^6.0.3" } } ================================================ FILE: packages/lezer/tests/compare-tree.js ================================================ import { Tree } from '@lezer/common' /** * @typedef {import('@lezer/common').Tree} Tree */ /** * @param {Tree} a * @param {Tree} b */ export function compareTree(a, b) { const curA = a.cursor(), curB = b.cursor() for (;;) { let mismatch = null, next = false if (curA.type !== curB.type) mismatch = `Node type mismatch (${curA.name} vs ${curB.name})` else if (curA.from !== curB.from) mismatch = `Start pos mismatch for ${curA.name}: ${curA.from} vs ${curB.from}` else if (curA.to !== curB.to) mismatch = `End pos mismatch for ${curA.name}: ${curA.to} vs ${curB.to}` else { next = curA.next() if (next !== curB.next()) mismatch = `Tree size mismatch` } if (mismatch) { const lines = [mismatch, 'a-:>', ...print(a), 'b-:>', ...print(b)] throw new Error(lines.join('\n')) } if (!next) break } } /** * @param {import('@lezer/common').TreeCursor | Tree} tree * @param {string} [prefix] */ export function print(tree, prefix = '') { const cur = tree instanceof Tree ? tree.cursor() : tree const lines = [`${prefix}${cur.name} (${cur.from}-${cur.to})`] if (cur.firstChild()) { do { lines.push(...print(cur, `${' '.repeat(prefix.length)}└╴`)) } while (cur.nextSibling()) cur.parent() } return lines } ================================================ FILE: packages/lezer/tests/incremental.test.js ================================================ /** * @typedef {{from: number, to?: number, insert?: string}[]} ChangeSpec */ import assert from 'node:assert' import { describe, it } from 'node:test' import { Tree, TreeFragment } from '@lezer/common' import { parser } from '../lib/index.js' import { compareTree } from './compare-tree.js' const doc = `* Header This is a /paragraph/. Still the same =paragraph=. #+begin_src js console.log('hello') #+end_src here is a link: [[https://example.com][link text]] ` const docLength = doc.length class State { constructor(doc, tree, fragments) { this.doc = doc this.tree = tree this.fragments = fragments } static start(doc) { const tree = parser.parse(doc) return new State(doc, tree, TreeFragment.addTree(tree)) } /** * @param {ChangeSpec[]} changes * @param {boolean} reparse */ update(changes, reparse = true) { let changed = [], doc = this.doc, off = 0 for (const { from, to = from, insert = '' } of changes) { doc = doc.slice(0, from) + insert + doc.slice(to) changed.push({ fromA: from - off, toA: to - off, fromB: from, toB: from + insert.length }) off += insert.length - (to - from) } const fragments = TreeFragment.applyChanges(this.fragments, changed, 2) if (!reparse) return new State(doc, Tree.empty, fragments) // return this const tree = parser.parse(doc, fragments) return new State(doc, tree, TreeFragment.addTree(tree, fragments)) } } // const state1 = State.start(doc) /** * @param {ChangeSpec} change * @param {boolean} [verbose=false] * @param {number} reuse */ function testChange(change, verbose = false, reuse = 10) { const state1 = State.start(doc) const state = state1.update(change) const updatedDoc = state.doc verbose && console.log('updatedDoc', updatedDoc) compareTree(state.tree, parser.parse(updatedDoc)) if (reuse) { const diff = overlap(state.tree, state1.tree) assert.ok(diff > reuse, `diff: ${diff}`) } } /** * @param {Tree} a * @param {Tree} b */ function overlap(a, b) { let inA = new Set(), shared = 0, sharingTo = 0 for (let cur = a.cursor(); cur.next(); ) if (cur.tree) inA.add(cur.tree) for (let cur = b.cursor(); cur.next(); ) if ( cur.tree && inA.has(cur.tree) && cur.type.is('Block') && cur.from >= sharingTo ) { shared += cur.to - cur.from sharingTo = cur.to } return Math.round((shared * 100) / b.length) } describe('incremential parsing', () => { // it.runOnly(true) it('can insert in the middle', () => { testChange([{ from: 2, to: 2, insert: 'bears' }]) }) it('can insert at the begining', () => { testChange([{ from: 0, to: 0, insert: 'bears' }]) }) it('can handle deletion', () => { testChange([{ from: 0, to: 5 }]) }) it('can appending at the end', () => { const size = doc.length testChange([{ from: size, to: size, insert: '* another heading' }]) }) it('can replace content', () => { testChange([{ from: 2, to: 8, insert: 'bears' }]) }) it('reuses nodes from the previous parse', () => { const state1 = State.start(doc) const state = state1.update([{ from: 2, to: 8, insert: 'bears' }]) const diff = overlap(state1.tree, state.tree) assert.ok(diff > 90, `diff: ${diff}`) console.log('hello test') }) it('can handle deleting a star', () => testChange([{ from: 0, to: 1 }])) // it('can reuse content for a change in a block context', () => {}) // it('can handle adding to a quoted block', () => {}) // it('can handle a change in a post-linkref paragraph', () => {}) // it('can handle a change in a paragraph-adjacent linkrefs', () => {}) it('can handle insertion at the eof', () => testChange([{ from: docLength, to: docLength, insert: '* h' }])) }) ================================================ FILE: packages/lezer/tsconfig.json ================================================ { "extends": "../../tsconfig.json" } ================================================ FILE: packages/loader/CHANGELOG.md ================================================ # Change Log ## 4.4.4 ### Patch Changes - bd2365a: fix types and linting - Updated dependencies [bd2365a] - @orgajs/orgx@2.6.1 ## 4.4.3 ### Patch Changes - Updated dependencies [a53cfea] - @orgajs/orgx@2.6.0 ## 4.4.2 ### Patch Changes - @orgajs/orgx@2.5.2 ## 4.4.1 ### Patch Changes - @orgajs/orgx@2.5.1 ## 4.4.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ### Patch Changes - Updated dependencies [188d30f] - @orgajs/orgx@2.5.0 ## 4.3.2 ### Patch Changes - e3ef3a5: build website with orga-build - Updated dependencies [e3ef3a5] - @orgajs/orgx@2.4.1 ## 4.3.1 ### Patch Changes - Updated dependencies [351f690] - @orgajs/orgx@2.4.0 ## 4.3.0 ### Minor Changes - d8861c2: update unified ecosystem ### Patch Changes - Updated dependencies [d8861c2] - @orgajs/orgx@2.3.0 ## 4.2.2 ### Patch Changes - @orgajs/orgx@2.2.2 ## 4.2.1 ### Patch Changes - @orgajs/orgx@2.2.1 ## 4.2.0 ### Minor Changes - ac322714: implement editor ### Patch Changes - Updated dependencies [ac322714] - @orgajs/orgx@2.2.0 ## 4.1.0 ### Minor Changes - 4d8efbb7: Add increamental parsing ability for the editor. ### Patch Changes - Updated dependencies [4d8efbb7] - @orgajs/orgx@2.1.0 ## 4.0.1 ### Patch Changes - Updated dependencies [1dbf674d] - @orgajs/orgx@2.0.1 ## 4.0.0 ### Major Changes - 176a3b5d: # Migrate most of the ecosystem to ESM We are excited to announce that we have migrated most of our ecosystem to ESM! This move was necessary as the unified ecosystem had already transitioned to ESM, leaving our orgajs system stuck on an older version if we wanted to stay on commonjs. We understand that this transition may come with some inevitable breaking changes, but we have done our best to make it as gentle as possible. In the past, ESM support in popular frameworks like webpack, gatsby, and nextjs was problematic, but the JS world has steadily moved forward, and we are now in a much better state. We have put in a lot of effort to bring this project up to speed, and we are happy to say that it's in a pretty good state now. We acknowledge that there are still some missing features that we will gradually add back over time. However, we feel that the changes are now in a great state to be released to the world. If you want to use the new versions, we recommend checking out the `examples` folder to get started. We understand that this upgrade path may not be compatible with older versions, and we apologize for any inconvenience this may cause. However, we encourage you to consider starting fresh, as the most important part of your site should always be your content (org-mode files). Thank you for your understanding, and we hope you enjoy the new and improved ecosystem! ### Patch Changes - Updated dependencies [176a3b5d] - @orgajs/orgx@2.0.0 ## 3.1.10 ### Patch Changes - Updated dependencies [eeccc870] - @orgajs/orgx@1.0.8 ## 3.1.9 ### Patch Changes - Updated dependencies [6c1ddb9f] - @orgajs/orgx@1.0.7 ## 3.1.8 ### Patch Changes - 4bde5155: tidy up dependencies - Updated dependencies [4bde5155] - @orgajs/orgx@1.0.6 ## 3.1.7 ### Patch Changes - @orgajs/orgx@1.0.5 ## 3.1.6 ### Patch Changes - @orgajs/orgx@1.0.4 ## 3.1.5 ### Patch Changes - Updated dependencies [cd7cac3d] - @orgajs/orgx@1.0.3 ## 3.1.4 ### Patch Changes - Updated dependencies [c8edd571] - @orgajs/orgx@1.0.2 ## 3.1.3 ### Patch Changes - 594bf16b: ## @orgajs/orgx Introducing new compiler `@orgajs/orgx`. It's a (almost) a direct port of [xdm](https://github.com/wooorm/xdm). Most of the packages have already adopted `@orgajs/orgx`. The important ones are: - `@orgajs/loader` - `@orgajs/next` - `gatsby-plugin-orga` - `gatsby-theme-orga-docs` - `@orgajs/playground'` `gatsby-transformer-orga` is still using the original compiler, since it has it's own ecosystem which requires some work to do a proper migration. That means the derivative packages around it are using the original compiler. - `gatsby-theme-orga-posts` - `gatsby-theme-orga-posts-core` ## theme-ui support `theme-ui` has `mdx` support builtin, and it's hard to do a clean extraction. So the package `@orgajs/theme-ui` is wrapping theme-ui, and provide orga specific tweaks. For gatsby, `gatsby-plugin-orga-theme-ui` is the equivalent of `gatsby-plugin-theme-ui`, but with orga support. - Updated dependencies [594bf16b] - @orgajs/orgx@1.0.1 ## 3.1.2 ### Patch Changes - @orgajs/reorg@3.1.1 ## 3.1.1 ### Patch Changes - 8c6f440b: - better layout support - rename MDXxxx to Orgaxxx ## 3.1.0 ### Minor Changes - eeea0c54: introduce new token: empty line ### Patch Changes - Updated dependencies [eeea0c54] - @orgajs/reorg@3.1.0 ## 3.0.1 ### Patch Changes - 6ed76057: # rename gatsby themes - gatsby-theme-orga -> gatsby-theme-orga-posts-core - gatsby-theme-blorg -> gatsby-theme-orga-posts # add example projects - gatsby-posts - gatsby-posts-core - 759e6149: # Bug Fixes - fix lexer for parsing headline with todo keyword - fix properties drawer issue - fix orga-theme-ui-preset package - fix gatsby-transformer-orga & gatsby-theme-blorg # Improved Playground - add `tokens` view - show node type in tree views - Updated dependencies [6ed76057] - Updated dependencies [759e6149] - @orgajs/reorg@3.0.1 ## 3.0.0 ### Major Changes - 8b02d10: # Features - more powerful and flexible lexer and parser - webpack support - `jsx` support - better code block rendering - better image processing in gatsby - updated examples - tons of bug fixes - brand new `gatsby-plugin-orga` ### Patch Changes - Updated dependencies [8b02d10] - @orgajs/reorg@3.0.0 All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. # [2.6.0](https://github.com/orgapp/orgajs/compare/v2.5.0...v2.6.0) (2021-08-28) ### Bug Fixes - fix examples ([bdcd265](https://github.com/orgapp/orgajs/commit/bdcd2655502a73800e8915ba09fd78452dff503f)) - remove prepublish step individually ([a75a6a9](https://github.com/orgapp/orgajs/commit/a75a6a9606421b66b6ef69b28e3fcb03a5ee282a)) - **website:** better code block ([9d5b3a2](https://github.com/orgapp/orgajs/commit/9d5b3a2d554672d22523727e89b2b5c60dc6233d)) # [2.5.0](https://github.com/orgapp/orgajs/compare/v2.4.9...v2.5.0) (2021-08-27) ### Bug Fixes - fix examples ([bdcd265](https://github.com/orgapp/orgajs/commit/bdcd2655502a73800e8915ba09fd78452dff503f)) ## [2.4.9](https://github.com/orgapp/orgajs/compare/v2.4.8...v2.4.9) (2021-07-13) **Note:** Version bump only for package @orgajs/loader ## [2.4.8](https://github.com/orgapp/orgajs/compare/v2.4.7...v2.4.8) (2021-04-26) **Note:** Version bump only for package @orgajs/loader ## [2.4.7](https://github.com/orgapp/orgajs/compare/v2.4.6...v2.4.7) (2021-04-26) **Note:** Version bump only for package @orgajs/loader ================================================ FILE: packages/loader/LICENSE.org ================================================ The MIT License (MIT) Copyright (c) 2015 gatsbyjs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: packages/loader/index.cjs ================================================ // stolen code from @mdx-js/loader /** * Webpack loader * * @todo once webpack supports ESM loaders, remove this wrapper. * * @this {LoaderContext} * @param {string} code */ module.exports = function orgLoader(code) { const callback = this.async() // Note that `import()` caches, so this should be fast enough. import('./lib/index.js').then((module) => module.loader.call(this, code, callback) ) } ================================================ FILE: packages/loader/index.d.ts ================================================ import type { ProcessorOptions } from '@orgajs/orgx' import type { LoaderContext } from 'webpack' /** * A Webpack loader to compile MDX to JavaScript. * * [Reference for Loader API](https://webpack.js.org/api/loaders/) * * @this {LoaderContext} * @param {string} value * The original module source code. * @returns {void} */ declare function orgLoader(this: LoaderContext, value: string): void export default orgLoader export type Options = ProcessorOptions ================================================ FILE: packages/loader/lib/index.js ================================================ /** * @typedef {import('webpack').LoaderContext} LoaderContext */ import path from 'node:path' import { createProcessor } from '@orgajs/orgx' /** * @param {string} source * @param {LoaderContext['callback']} callback * @this {LoaderContext} */ export async function loader(source, callback) { const processor = createProcessor({ development: this.mode === 'development', ...this.getOptions() }) try { const file = await processor.process({ value: source, path: this.resourcePath }) callback(undefined, file.value, file.map) } catch (error) { const fpath = path.relative(this.context, this.resourcePath) error.message = `${fpath}:${error.name}: ${error.message}` callback(error) } } ================================================ FILE: packages/loader/package.json ================================================ { "name": "@orgajs/loader", "version": "4.4.4", "description": "Load org-mode content through orga.", "type": "module", "main": "./index.cjs", "types": "index.d.ts", "files": [ "lib/", "index.cjs", "index.d.ts" ], "author": "Xiaoxing Hu ", "license": "MIT", "homepage": "https://github.com/orgapp/orgajs/tree/main/packages/loader#readme", "repository": { "type": "git", "url": "https://github.com/orgapp/orgajs.git", "directory": "packages/loader" }, "scripts": { "test": "node --test tests/*.test.js" }, "dependencies": { "@orgajs/orgx": "workspace:^", "vfile-reporter": "^8.1.1" }, "devDependencies": { "@orgajs/react": "workspace:^", "@types/node": "^25.3.2", "babel-loader": "^9.1.3", "html-loader": "^4.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", "webpack": "^5.104.1" } } ================================================ FILE: packages/loader/tests/compiler.js ================================================ import { promises as fs } from 'node:fs' import { fileURLToPath } from 'node:url' import { promisify } from 'node:util' import webpack from 'webpack' export async function compile(fixture, options = {}) { const base = new URL('.', import.meta.url) const result = await promisify(webpack)({ // @ts-expect-error TODO: webpack types miss support for `context`. context: fileURLToPath(base), entry: `./${fixture}`, mode: 'none', output: { path: fileURLToPath(base), filename: 'bundle.js' }, module: { rules: [ { test: /\.org$/, use: [ // { // loader: 'babel-loader', // options: { // configFile: false, // plugins: [ // '@babel/plugin-transform-runtime', // '@babel/plugin-syntax-jsx', // '@babel/plugin-transform-react-jsx', // ], // }, // }, { // loader: path.resolve(__dirname, '../dist'), loader: fileURLToPath(new URL('../index.cjs', import.meta.url)), options } ] } ] } }) // cleanup await fs.unlink(new URL('bundle.js', base)) return result } ================================================ FILE: packages/loader/tests/example.md ================================================ # hello world this is some content. ================================================ FILE: packages/loader/tests/fixture.org ================================================ #+title: hello world * TODO headline one #+begin_export jsx
in a box
#+end_export ================================================ FILE: packages/loader/tests/loader.test.js ================================================ import * as assert from 'node:assert' import test from 'node:test' import { compile } from './compiler.js' test('basic org-mode parsing', async () => { const stats = await compile('fixture.org', { name: 'Alice' }) // wait for 3 seconds await new Promise((resolve) => setTimeout(resolve, 3000)) console.log('after the wait') const output = `${stats.toJson({ source: true }).modules[0].source}` assert.equal( output.trim(), ` /*@jsxRuntime automatic @jsxImportSource react*/ import {jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime"; export const title = 'hello world'; function _createOrgContent(props) { const _components = Object.assign({ div: "div", h1: "h1" }, props.components); return _jsxs(_components.div, { className: "section", children: [_jsx(_components.h1, { children: "headline one" }), _jsx(_components.div, { style: { color: 'red' }, children: "in a box" })] }); } function OrgContent(props = {}) { const {wrapper: OrgLayout} = props.components || ({}); return OrgLayout ? _jsx(OrgLayout, { ...props, children: _jsx(_createOrgContent, { ...props }) }) : _createOrgContent(props); } export default OrgContent; `.trim() ) }) ================================================ FILE: packages/metadata/CHANGELOG.md ================================================ # @orgajs/metadata ## 2.4.1 ### Patch Changes - bd2365a: fix types and linting ## 2.4.0 ### Minor Changes - a53cfea: all about the editor This release improves the editor with new fold/shift/todo actions and settings, while also refactoring orga tokenization/parsing and lezer conversion to improve TODO handling, context hashing, and tree generation consistency. ## 2.3.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ## 2.2.0 ### Minor Changes - d8861c2: update unified ecosystem ## 2.1.0 ### Minor Changes - 4d8efbb7: Add increamental parsing ability for the editor. ## 2.0.0 ### Major Changes - 176a3b5d: # Migrate most of the ecosystem to ESM We are excited to announce that we have migrated most of our ecosystem to ESM! This move was necessary as the unified ecosystem had already transitioned to ESM, leaving our orgajs system stuck on an older version if we wanted to stay on commonjs. We understand that this transition may come with some inevitable breaking changes, but we have done our best to make it as gentle as possible. In the past, ESM support in popular frameworks like webpack, gatsby, and nextjs was problematic, but the JS world has steadily moved forward, and we are now in a much better state. We have put in a lot of effort to bring this project up to speed, and we are happy to say that it's in a pretty good state now. We acknowledge that there are still some missing features that we will gradually add back over time. However, we feel that the changes are now in a great state to be released to the world. If you want to use the new versions, we recommend checking out the `examples` folder to get started. We understand that this upgrade path may not be compatible with older versions, and we apologize for any inconvenience this may cause. However, we encourage you to consider starting fresh, as the most important part of your site should always be your content (org-mode files). Thank you for your understanding, and we hope you enjoy the new and improved ecosystem! ## 1.0.2 ### Patch Changes - eeccc870: - get image links out of paragraph - some other minor fixes ## 1.0.1 ### Patch Changes - 594bf16b: ## @orgajs/orgx Introducing new compiler `@orgajs/orgx`. It's a (almost) a direct port of [xdm](https://github.com/wooorm/xdm). Most of the packages have already adopted `@orgajs/orgx`. The important ones are: - `@orgajs/loader` - `@orgajs/next` - `gatsby-plugin-orga` - `gatsby-theme-orga-docs` - `@orgajs/playground'` `gatsby-transformer-orga` is still using the original compiler, since it has it's own ecosystem which requires some work to do a proper migration. That means the derivative packages around it are using the original compiler. - `gatsby-theme-orga-posts` - `gatsby-theme-orga-posts-core` ## theme-ui support `theme-ui` has `mdx` support builtin, and it's hard to do a clean extraction. So the package `@orgajs/theme-ui` is wrapping theme-ui, and provide orga specific tweaks. For gatsby, `gatsby-plugin-orga-theme-ui` is the equivalent of `gatsby-plugin-theme-ui`, but with orga support. ================================================ FILE: packages/metadata/README.org ================================================ #+title: @orgajs/metadata ================================================ FILE: packages/metadata/index.js ================================================ /** * @typedef {import('./lib/index.js').Metadata} Metadata */ export { parse } from './lib/index.js' ================================================ FILE: packages/metadata/lib/index.js ================================================ /** * TODO: more types? * @typedef {string} Value * @typedef {Record} Metadata */ const TO_DISCARD = [ 'caption', 'header', 'name', 'plot', 'results', /^attr_\w+/i, // Affiliated Keywords /^begin_\w+/i, /^end_\w+/i, 'begin', 'end', // blocks 'call', // call 'jsx' // orga's jsx support ] /** * @param {string} key * @returns {boolean} */ function shouldDiscard(key) { return !!TO_DISCARD.find((test) => { if (typeof test === 'string') { return test === key.toLowerCase() } return test.test(key) }) } /** * trim whitespaces and strip quotes if necessary * @param {string} text * @returns {Value} */ function processValue(text) { return text.trim().replace(/^["'](.+(?=["']$))["']$/, '$1') } /** * @param {Metadata} data */ function pushTo(data) { /** * @param {string} _key * @param {string} _value * @returns {Metadata} */ return function (_key, _value) { const key = _key.toLowerCase() const value = processValue(_value) const existing = data[key] if (existing) { if (Array.isArray(existing)) { existing.push(value) } else { data[key] = [existing, value] } } else { data[key] = value } return data } } /** * @param {string} text * @returns {Metadata} */ export function parse(text) { const matches = text.matchAll(/^\s*#\+(\S+):(.*)$/gm) return [...matches].reduce((data, [, key, value]) => { if (shouldDiscard(key)) return data return pushTo(data)(key, value) }, {}) } ================================================ FILE: packages/metadata/package.json ================================================ { "name": "@orgajs/metadata", "version": "2.4.1", "description": "extract metadata info from org file", "type": "module", "author": "Xiaoxing Hu ", "license": "MIT", "homepage": "https://github.com/orgapp/orgajs/tree/main/packages/metadata#readme", "repository": { "type": "git", "url": "https://github.com/orgapp/orgajs.git", "directory": "packages/metadata" }, "files": [ "lib", "index.js", "index.d.ts", "index.d.ts.map" ], "exports": "./index.js", "scripts": { "test": "node --test --no-warnings tests/test.js" }, "devDependencies": { "@types/node": "^25.3.2", "tsx": "^4.19.2", "typescript": "^5.9.2" } } ================================================ FILE: packages/metadata/tests/test.js ================================================ import * as assert from 'node:assert' import { describe, it } from 'node:test' import { parse } from '../index.js' describe('metadata parser', () => { it('works', () => { assert.deepEqual(parse('#+title: hello world'), { title: 'hello world' }) }) it('can handle multiple entries', () => { assert.deepEqual( parse(` #+include: file1.org #+include: ../file2.org `), { include: ['file1.org', '../file2.org'] } ) }) it('can handle spaces at the front', () => { assert.deepEqual( parse(` #+title: orga #+keywords: parser ast `), { title: 'orga', keywords: 'parser ast' } ) }) it('transform keys to lowercase', () => { assert.deepEqual( parse(` #+TODO: TODO NEXT | DONE #+todo: DRAFT | PUBLISHED `), { todo: ['TODO NEXT | DONE', 'DRAFT | PUBLISHED'] } ) }) it('strips quotes if necessary', () => { assert.deepEqual( parse(` #+include: "./file1.org" #+include: './file2.org' #+desc: it's good `), { include: ['./file1.org', './file2.org'], desc: "it's good" } ) }) }) ================================================ FILE: packages/metadata/tsconfig.json ================================================ { "extends": "../../tsconfig.json" } ================================================ FILE: packages/next/README.org ================================================ * @orgajs/next Next.js integration is now maintained in a standalone repository: - [[https://github.com/orgapp/orga-next][orgapp/orga-next]] Package name remains =@orgajs/next=. For usage and setup instructions, see: - [[https://github.com/orgapp/orga-next/blob/main/README.org][README.org]] - [[https://orga.js.org/guides/next][Next.js guide]] ================================================ FILE: packages/node-loader/CHANGELOG.md ================================================ # @orgajs/node-loader ## 1.1.5 ### Patch Changes - bd2365a: fix types and linting - Updated dependencies [bd2365a] - @orgajs/orgx@2.6.1 ## 1.1.4 ### Patch Changes - Updated dependencies [a53cfea] - @orgajs/orgx@2.6.0 ## 1.1.3 ### Patch Changes - @orgajs/orgx@2.5.2 ## 1.1.2 ### Patch Changes - @orgajs/orgx@2.5.1 ## 1.1.1 ### Patch Changes - b73e6b3: fix builder ## 1.1.0 ### Minor Changes - 188d30f: - migrate most of modules to js - fix types during the process - remove unmaintained modules ### Patch Changes - Updated dependencies [188d30f] - @orgajs/orgx@2.5.0 ## 1.0.1 ### Patch Changes - Updated dependencies [e3ef3a5] - @orgajs/orgx@2.4.1 ## 1.0.0 ### Major Changes - 351f690: introduce @orgajs/node-loader, @orgajs/esbuild, @orgajs/build - @orgajs/node-loader : the nodejs loader for org-mode files - @orgajs/esbuild : esbuild plugin - @orgajs/build : static site generator, a.k.a orga-build ### Patch Changes - Updated dependencies [351f690] - @orgajs/orgx@2.4.0 ================================================ FILE: packages/node-loader/index.js ================================================ /** * @import {LoadFnOutput, LoadHook, LoadHookContext} from 'node:module' * @import {ProcessorOptions} from '@orgajs/orgx' */ /** * @typedef {Parameters[2]} NextLoad * Next. * * @typedef {ProcessorOptions} Options * Configuration. * */ import fs from 'node:fs/promises' import { createProcessor } from '@orgajs/orgx' import { SourceMapGenerator } from 'source-map' import { VFile } from 'vfile' import { reporter } from 'vfile-reporter' /** * Create Node.js hooks to handle org files. * * @param {Readonly | null | undefined} [loaderOptions] * Configuration (optional). * @returns * Node.js hooks. */ export function createLoader(loaderOptions) { let settings = configure(loaderOptions || {}) return { initialize, load } /** * @param {Readonly | null | undefined} options */ async function initialize(options) { settings = configure({ ...loaderOptions, ...options }) } /** * Load `file:` URLs to MD(X) files. * * @param {string} href * URL. * @param {LoadHookContext} context * Context. * @param {NextLoad} nextLoad * Next or default `load` function. * @returns {Promise} * Result. * @satisfies {LoadHook} */ async function load(href, context, nextLoad) { const url = new URL(href) const { compile } = settings if (url.protocol === 'file:' && /.org$/.test(url.pathname)) { const value = await fs.readFile(url) const file = await compile(new VFile({ value, path: url })) if (file.messages.length > 0) { console.error(reporter(file)) } let source = String(file) source += '\n//# sourceMappingURL=data:application/json;base64,' + Buffer.from(JSON.stringify(file.map)).toString('base64') + '\n' return { format: 'module', shortCircuit: true, source } } return nextLoad(href, context) } } /** * @param {Options} options */ function configure(options) { const processor = createProcessor({ development: true, ...options, SourceMapGenerator }) /** * @param {import('vfile').Compatible} file */ function compile(file) { return processor.process(file) } return { compile } } const defaultLoader = createLoader() /** * Pass options to the loader. */ export const initialize = defaultLoader.initialize /** * Load `file:` URLs to org files. */ export const load = defaultLoader.load ================================================ FILE: packages/node-loader/package.json ================================================ { "name": "@orgajs/node-loader", "version": "1.1.5", "description": "", "type": "module", "files": [ "lib", "index.js", "index.d.ts", "index.d.ts.map" ], "exports": "./index.js", "scripts": {}, "keywords": [], "author": "Xiaoxing Hu ", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/orgapp/orgajs.git", "directory": "packages/node-loader" }, "dependencies": { "@orgajs/orgx": "workspace:^", "source-map": "^0.7.4", "vfile": "^6.0.3", "vfile-reporter": "^8.1.1" }, "devDependencies": { "@types/node": "^25.3.2" } } ================================================ FILE: packages/oast-to-hast/.projectile ================================================ ================================================ FILE: packages/oast-to-hast/CHANGELOG.md ================================================ # Change Log ## 4.5.3 ### Patch Changes - 850bcf9: fix: use native anchor for external links to prevent wouter pushState SecurityError ## 4.5.2 ### Patch Changes - be20652: expose rehypePlugins in orga-build ## 4.5.1 ### Patch Changes - bd2365a: fix types and linting - Updated dependencies [bd2365a] - orga@4.7.1 ## 4.5.0 ### Minor Changes - 761c484: Preserve inline markup in quote and center blocks. ### Patch Changes - Updated dependencies [761c484] - orga@4.7.0 ## 4.4.3 ### Patch Changes - 68430c7: fix newline in paragraph ## 4.4.2 ### Patch Changes - d8da621: fix invalid html structure with image/video ## 4.4.1 ### Patch Changes - 20f5a03: fix: render video links as `