├── js ├── loader │ ├── .gitignore │ ├── src │ │ └── router │ │ │ └── index.ts │ └── package.json ├── luna │ ├── .gitignore │ ├── examples_preact │ │ ├── .gitignore │ │ ├── vite.config.ts │ │ ├── tsconfig.json │ │ ├── index.html │ │ ├── package.json │ │ └── app.tsx │ ├── src │ │ └── jsx-dev-runtime.ts │ ├── examples │ │ ├── vite.config.ts │ │ ├── tsconfig.json │ │ ├── index.html │ │ ├── package.json │ │ └── app.tsx │ ├── tsconfig.json │ ├── tsdown.config.ts │ └── package.json ├── astra │ ├── bin │ │ └── astra.mjs │ ├── src │ │ ├── index.ts │ │ ├── worker.ts │ │ └── cli.ts │ ├── tsconfig.json │ ├── tsdown.config.ts │ ├── assets │ │ └── scripts │ │ │ ├── theme.js │ │ │ ├── sidebar.js │ │ │ ├── toc.js │ │ │ └── wc-loader.js │ └── package.json └── wcssr │ ├── vitest.bench.config.ts │ ├── src │ ├── server-entry.ts │ ├── index.ts │ └── client-entry.ts │ ├── vitest.browser.config.ts │ └── tsconfig.json ├── sol.config.json ├── src ├── core │ ├── ssg │ │ ├── moon.pkg.json │ │ └── renderer_type.mbt │ ├── config │ │ └── moon.pkg.json │ ├── env │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ ├── routes │ │ └── moon.pkg.json │ └── parser │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti ├── internal │ ├── json_utils │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ ├── utils │ │ ├── moon.pkg.json │ │ ├── pkg.generated.mbti │ │ └── wc_name.mbt │ └── node_fs_adapter │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti ├── tests │ ├── json_renderer │ │ ├── README.mb │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ ├── ssr_test_component │ │ ├── moon.pkg.json │ │ ├── pkg.generated.mbti │ │ └── ssr_test_component.mbt │ ├── router_csr │ │ ├── pkg.generated.mbti │ │ └── moon.pkg.json │ ├── e2e_server │ │ ├── pkg.generated.mbti │ │ └── moon.pkg.json │ ├── counter_client │ │ ├── pkg.generated.mbti │ │ └── moon.pkg.json │ ├── counter_component │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ └── browser_components │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti ├── astra │ ├── cli │ │ ├── cli_utils.mbt │ │ └── pkg.generated.mbti │ ├── shiki │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ ├── assets │ │ ├── moon.pkg.json │ │ ├── pkg.generated.mbti │ │ └── scripts │ │ │ ├── theme.js │ │ │ ├── sidebar.js │ │ │ ├── toc.js │ │ │ └── wc-loader.js │ ├── markdown │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ ├── astra_worker │ │ ├── pkg.generated.mbti │ │ ├── main.mbt │ │ └── moon.pkg.json │ ├── moon.pkg.json │ ├── tree │ │ ├── moon.pkg.json │ │ ├── pkg.generated.mbti │ │ └── sitemap.mbt │ ├── builder_pool │ │ └── moon.pkg.json │ ├── routes │ │ └── moon.pkg.json │ ├── docs │ │ ├── ja │ │ │ ├── index.md │ │ │ └── guide │ │ │ │ └── index.md │ │ ├── index.md │ │ └── guide │ │ │ └── index.md │ ├── sol.config.json │ ├── generator │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ └── README.md ├── luna │ ├── signal │ │ └── moon.pkg.json │ ├── serialize │ │ └── moon.pkg.json │ ├── moon.pkg.json │ ├── routes │ │ ├── moon.pkg.json │ │ ├── routes_match.mbt │ │ └── pkg.generated.mbti │ ├── render │ │ ├── moon.pkg.json │ │ └── pkg.generated.mbti │ └── _check.mbt ├── sol │ ├── cli │ │ ├── cli_utils.mbt │ │ └── moon.pkg.json │ ├── isr │ │ ├── moon.pkg.json │ │ ├── pkg.generated.mbti │ │ └── memory_cache.mbt │ ├── middleware │ │ └── moon.pkg.json │ ├── action │ │ └── moon.pkg.json │ ├── moon.pkg.json │ └── router │ │ └── moon.pkg.json ├── moon.pkg.json ├── platform │ ├── dom │ │ ├── router │ │ │ ├── moon.pkg.json │ │ │ └── pkg.generated.mbti │ │ ├── portal │ │ │ ├── moon.pkg.json │ │ │ └── pkg.generated.mbti │ │ ├── element │ │ │ └── moon.pkg.json │ │ ├── moon.pkg.json │ │ ├── client │ │ │ ├── repair │ │ │ │ ├── moon.pkg.json │ │ │ │ └── experimental_hydrate_wbtest.mbt │ │ │ ├── moon.pkg.json │ │ │ └── pkg.generated.mbti │ │ └── debounced.mbt │ ├── js │ │ ├── cache │ │ │ ├── moon.pkg.json │ │ │ └── pkg.generated.mbti │ │ ├── fs_adapter │ │ │ ├── moon.pkg.json │ │ │ └── pkg.generated.mbti │ │ └── stream_renderer │ │ │ ├── moon.pkg.json │ │ │ └── pkg.generated.mbti │ ├── server_dom │ │ ├── moon.pkg.json │ │ ├── element │ │ │ ├── moon.pkg.json │ │ │ ├── sol_link.mbt │ │ │ └── outlet.mbt │ │ ├── wc_island.mbt │ │ ├── island.mbt │ │ ├── pkg.generated.mbti │ │ └── types.mbt │ └── README.md ├── _bench │ ├── pkg.generated.mbti │ └── moon.pkg.json ├── examples │ ├── wc │ │ ├── pkg.generated.mbti │ │ └── moon.pkg.json │ ├── game │ │ ├── pkg.generated.mbti │ │ ├── moon.pkg.json │ │ └── index.html │ ├── spa │ │ ├── pkg.generated.mbti │ │ └── moon.pkg.json │ ├── hello_luna │ │ ├── pkg.generated.mbti │ │ ├── moon.pkg.json │ │ ├── index.html │ │ └── main.mbt │ ├── browser_router │ │ ├── pkg.generated.mbti │ │ └── moon.pkg.json │ ├── todomvc │ │ ├── pkg.generated.mbti │ │ ├── moon.pkg.json │ │ └── storage.mbt │ └── wiki │ │ └── moon.pkg.json └── stella │ ├── moon.pkg.json │ └── README.md ├── .claude └── skills │ └── mooncheat │ ├── moon.mod.json │ └── SKILL.md ├── .vscode └── settings.json ├── examples ├── astra_app │ ├── moon.pkg.json │ ├── docs │ │ ├── ssr_test_component │ │ │ ├── page.json │ │ │ ├── moon.pkg.json │ │ │ └── ssr_test_component.mbt │ │ ├── tsx_demo │ │ │ ├── page.json │ │ │ └── tsx_demo.tsx │ │ ├── posts │ │ │ ├── _slug_ │ │ │ │ ├── page.json │ │ │ │ └── index.md │ │ │ ├── hello-world │ │ │ │ └── index.md │ │ │ └── index.md │ │ ├── foo.html │ │ ├── wiki │ │ │ └── index.md │ │ ├── about │ │ │ └── index.md │ │ ├── index.md │ │ └── guide │ │ │ └── index.md │ ├── moon.mod.json │ └── astra.json ├── sol_app │ ├── sol.config.json │ ├── app │ │ ├── client │ │ │ ├── _using.mbt │ │ │ ├── moon.pkg.json │ │ │ └── counter.mbt │ │ ├── server │ │ │ ├── _using.mbt │ │ │ ├── about.mbt │ │ │ └── moon.pkg.json │ │ └── __gen__ │ │ │ └── types │ │ │ ├── moon.pkg.json │ │ │ └── types.mbt │ ├── .gitignore │ ├── moon.mod.json │ ├── playwright.config.ts │ ├── package.json │ ├── scripts │ │ └── patch-for-cloudflare.js │ └── static │ │ └── loader.js └── ssg_test │ ├── sol.config.json │ └── package.json ├── fixtures └── astra_files │ ├── basic │ └── docs │ │ ├── 01_guide │ │ ├── 02_usage.md │ │ ├── index.md │ │ └── 01_installation.md │ │ ├── index.md │ │ └── 00_overview │ │ ├── 01_features.md │ │ └── index.md │ ├── mixed_prefix │ └── docs │ │ └── tutorial │ │ ├── 01_basics.md │ │ ├── advanced.md │ │ ├── index.md │ │ └── flow_control.md │ └── nested_tutorial │ └── docs │ └── 20_moonbit │ ├── 01_tutorial │ ├── 01_basics.md │ ├── index.md │ └── 02_signals.md │ ├── 02_api │ ├── 01_signals.md │ └── index.md │ └── index.md ├── e2e ├── visual-snapshots.test.ts-snapshots │ ├── wc-style.png │ ├── wc-todo.png │ ├── spa-style.png │ ├── wc-counter.png │ ├── spa-counter.png │ ├── spa-full-page.png │ ├── todomvc-empty.png │ ├── wc-full-page.png │ ├── todomvc-footer.png │ ├── spa-modal-closed.png │ ├── todomvc-with-todos.png │ └── wc-nested-parent.png ├── astra │ ├── dark-theme.test.ts-snapshots │ │ ├── code-block-dark-darwin.png │ │ └── code-block-light-darwin.png │ └── playwright.config.ts ├── browser-router │ └── browser-router.test.ts-snapshots │ │ ├── users-page-layout.txt │ │ └── home-page-structure.txt ├── sol-app │ └── playwright.config.mts ├── playwright.config-sol.mts ├── template-app │ └── playwright.config.mts ├── wc_counter_csr.test.ts ├── coverage-fixture.mjs └── playwright.config.mts ├── demo-src ├── game_react │ ├── vite.config.ts │ ├── src │ │ ├── main.tsx │ │ └── index.css │ ├── index.html │ ├── package.json │ └── tsconfig.json ├── browser_router │ └── index.html ├── game │ └── index.html └── wc │ └── index.html ├── pnpm-workspace.yaml ├── tests └── cloudflare │ └── wrangler.toml ├── .gitignore ├── docs ├── 00_introduction │ ├── index.md │ ├── 02_getting-started │ │ └── index.md │ └── 03_faq │ │ └── index.md ├── ja │ ├── 04_stella │ │ └── index.md │ ├── 01_luna │ │ └── index.md │ ├── 03_sol │ │ └── index.md │ ├── 05_tutorial-moonbit │ │ └── index.md │ └── 06_tutorial-js │ │ └── index.md ├── 04_stella │ └── index.md ├── 01_luna │ ├── 02_api-js │ │ └── index.md │ ├── index.md │ ├── 03_api-moonbit │ │ └── index.md │ └── 04_tutorial-js │ │ └── index.md ├── internal │ └── ja │ │ └── introduce-luna-mbt.md └── components │ └── my-counter.js ├── vitest.config.cloudflare.ts ├── tsconfig.json ├── moon.mod.json ├── experiments ├── webcomponents_ssr │ └── demo-component.css ├── css-minify │ └── README.md └── view_transition │ ├── index.html │ ├── bar.html │ └── foo.html ├── vitest.config.ts ├── vitest.browser.config.ts └── scripts └── lib └── cov-reporter ├── reporters └── console.ts ├── sourcemap.ts ├── parsers └── moonbit.ts └── types.ts /js/loader/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /js/luna/.gitignore: -------------------------------------------------------------------------------- 1 | __screenshots__ 2 | dist/ -------------------------------------------------------------------------------- /sol.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "islands": [] 3 | } 4 | -------------------------------------------------------------------------------- /src/core/ssg/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [] 3 | } 4 | -------------------------------------------------------------------------------- /js/luna/examples_preact/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /.claude/skills/mooncheat/moon.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mizchi/mooncheat" 3 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.experimental.useTsgo": false 3 | } -------------------------------------------------------------------------------- /js/astra/bin/astra.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import "../dist/cli.js"; 3 | -------------------------------------------------------------------------------- /examples/astra_app/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": false, 3 | "import": [] 4 | } 5 | -------------------------------------------------------------------------------- /js/astra/src/index.ts: -------------------------------------------------------------------------------- 1 | // @luna_ui/astra - MoonBit Island Architecture SSG Framework 2 | export {}; 3 | -------------------------------------------------------------------------------- /src/core/config/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | "mizchi/luna/internal/json_utils" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/internal/json_utils/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js", 4 | "native" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /fixtures/astra_files/basic/docs/01_guide/02_usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Usage 3 | --- 4 | 5 | # Usage 6 | 7 | How to use. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/basic/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | --- 4 | 5 | # Welcome 6 | 7 | This is the home page. 8 | -------------------------------------------------------------------------------- /src/tests/json_renderer/README.mb: -------------------------------------------------------------------------------- 1 | ## json diff patch renderer 2 | 3 | pre-reference implementation for any platforms 4 | -------------------------------------------------------------------------------- /src/core/env/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js", 4 | "wasm-gc", 5 | "native" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/basic/docs/01_guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Guide 3 | --- 4 | 5 | # Guide 6 | 7 | Getting started guide. 8 | -------------------------------------------------------------------------------- /src/internal/utils/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js", 4 | "wasm-gc", 5 | "native" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/basic/docs/00_overview/01_features.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Features 3 | --- 4 | 5 | # Features 6 | 7 | List of features. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/mixed_prefix/docs/tutorial/01_basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Basics 3 | --- 4 | 5 | # Basics 6 | 7 | Learn the basics. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/mixed_prefix/docs/tutorial/advanced.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced 3 | --- 4 | 5 | # Advanced 6 | 7 | Advanced topics. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/basic/docs/00_overview/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | --- 4 | 5 | # Overview 6 | 7 | This is the overview section. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/basic/docs/01_guide/01_installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | --- 4 | 5 | # Installation 6 | 7 | How to install. 8 | -------------------------------------------------------------------------------- /src/astra/cli/cli_utils.mbt: -------------------------------------------------------------------------------- 1 | // CLI utilities 2 | 3 | ///| 4 | fn console_error(msg : String) -> Unit { 5 | @console.error(@js.any(msg)) 6 | } 7 | -------------------------------------------------------------------------------- /src/luna/signal/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [], 3 | "test-import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/sol/cli/cli_utils.mbt: -------------------------------------------------------------------------------- 1 | // CLI utilities 2 | 3 | ///| 4 | fn console_error(msg : String) -> Unit { 5 | @console.error(@js.any(msg)) 6 | } 7 | -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/wc-style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/wc-style.png -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/wc-todo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/wc-todo.png -------------------------------------------------------------------------------- /fixtures/astra_files/mixed_prefix/docs/tutorial/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorial 3 | --- 4 | 5 | # Tutorial 6 | 7 | Introduction to the tutorial. 8 | -------------------------------------------------------------------------------- /src/sol/isr/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/js/core", "alias": "js" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/spa-style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/spa-style.png -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/wc-counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/wc-counter.png -------------------------------------------------------------------------------- /fixtures/astra_files/mixed_prefix/docs/tutorial/flow_control.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Flow Control 3 | --- 4 | 5 | # Flow Control 6 | 7 | About flow control. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/nested_tutorial/docs/20_moonbit/01_tutorial/01_basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Basics 3 | --- 4 | 5 | # Basics 6 | 7 | Learn the basics. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/nested_tutorial/docs/20_moonbit/01_tutorial/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorial 3 | --- 4 | 5 | # Tutorial 6 | 7 | Step by step guide. 8 | -------------------------------------------------------------------------------- /src/astra/shiki/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/js/core", "alias": "core" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/spa-counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/spa-counter.png -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/spa-full-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/spa-full-page.png -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/todomvc-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/todomvc-empty.png -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/wc-full-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/wc-full-page.png -------------------------------------------------------------------------------- /fixtures/astra_files/nested_tutorial/docs/20_moonbit/01_tutorial/02_signals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Signals 3 | --- 4 | 5 | # Signals 6 | 7 | Reactive signals. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/nested_tutorial/docs/20_moonbit/02_api/01_signals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Signal API 3 | --- 4 | 5 | # Signal API 6 | 7 | Signal functions. 8 | -------------------------------------------------------------------------------- /fixtures/astra_files/nested_tutorial/docs/20_moonbit/02_api/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | --- 4 | 5 | # API Reference 6 | 7 | API documentation. 8 | -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/todomvc-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/todomvc-footer.png -------------------------------------------------------------------------------- /js/luna/src/jsx-dev-runtime.ts: -------------------------------------------------------------------------------- 1 | // JSX Dev Runtime - re-exports jsx-runtime for development mode 2 | export { jsx, jsxs, Fragment, jsxDEV } from "./jsx-runtime"; 3 | -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/spa-modal-closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/spa-modal-closed.png -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/todomvc-with-todos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/todomvc-with-todos.png -------------------------------------------------------------------------------- /e2e/visual-snapshots.test.ts-snapshots/wc-nested-parent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/visual-snapshots.test.ts-snapshots/wc-nested-parent.png -------------------------------------------------------------------------------- /examples/astra_app/docs/ssr_test_component/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "SSR Test Component", 3 | "description": "Demo component page with SSR", 4 | "ssr": true 5 | } 6 | -------------------------------------------------------------------------------- /fixtures/astra_files/nested_tutorial/docs/20_moonbit/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorial (MoonBit) 3 | --- 4 | 5 | # MoonBit Tutorial 6 | 7 | Learn Luna with MoonBit. 8 | -------------------------------------------------------------------------------- /src/luna/serialize/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | "mizchi/luna/luna/signal", 4 | { "path": "mizchi/luna/internal/utils", "alias": "utils" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /examples/astra_app/docs/tsx_demo/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "TSX Demo", 3 | "description": "Demo of TSX component SSR", 4 | "ssr": true, 5 | "renderer": "react" 6 | } 7 | -------------------------------------------------------------------------------- /src/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "core" }, 4 | { "path": "mizchi/luna/luna/signal", "alias": "signal" } 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /e2e/astra/dark-theme.test.ts-snapshots/code-block-dark-darwin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/astra/dark-theme.test.ts-snapshots/code-block-dark-darwin.png -------------------------------------------------------------------------------- /e2e/astra/dark-theme.test.ts-snapshots/code-block-light-darwin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi/luna.mbt/HEAD/e2e/astra/dark-theme.test.ts-snapshots/code-block-light-darwin.png -------------------------------------------------------------------------------- /js/luna/examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | 3 | export default defineConfig({ 4 | root: './', 5 | build: { 6 | outDir: 'dist', 7 | }, 8 | }); -------------------------------------------------------------------------------- /src/luna/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | "mizchi/luna/luna/signal" 4 | ], 5 | "supported-targets": [ 6 | "js", 7 | "wasm-gc", 8 | "native" 9 | ] 10 | } -------------------------------------------------------------------------------- /demo-src/game_react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | }) 7 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "js/*" 3 | - "js/luna/examples" 4 | - "js/luna/examples_preact" 5 | 6 | onlyBuiltDependencies: 7 | - esbuild 8 | - sharp 9 | - workerd 10 | -------------------------------------------------------------------------------- /src/tests/json_renderer/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "luna" } 4 | ], 5 | "test-import": [ 6 | "mizchi/luna/luna/signal" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/astra_app/docs/posts/_slug_/page.json: -------------------------------------------------------------------------------- 1 | { 2 | "staticParams": [ 3 | { "slug": "hello-world" }, 4 | { "slug": "getting-started" }, 5 | { "slug": "advanced-topics" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/sol_app/sol.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "islands": ["app/client"], 3 | "routes": "app/server", 4 | "runtime": "cloudflare", 5 | "output": "app/__gen__", 6 | "client_auto_exports": false 7 | } 8 | -------------------------------------------------------------------------------- /js/astra/src/worker.ts: -------------------------------------------------------------------------------- 1 | // Astra Worker Entry Point 2 | // This file bundles the MoonBit worker for npm distribution 3 | 4 | import "../../../target/js/release/build/astra/astra_worker/astra_worker.js"; 5 | -------------------------------------------------------------------------------- /src/platform/dom/router/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna/routes", "alias": "routes" }, 5 | "mizchi/luna/luna/signal" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/astra/assets/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/astra", "alias": "astra" }, 5 | { "path": "mizchi/js/node/fs", "alias": "fs" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/sol_app/app/client/_using.mbt: -------------------------------------------------------------------------------- 1 | ///| 2 | /// Hydration functions are auto-generated by `sol generate`. 3 | using @element {type DomNode, div, span, button, input, form, label, text, text_of, events, use_style} 4 | -------------------------------------------------------------------------------- /tests/cloudflare/wrangler.toml: -------------------------------------------------------------------------------- 1 | # Wrangler config for Cloudflare Workers Vitest tests 2 | name = "luna-test-worker" 3 | main = "./worker.ts" 4 | compatibility_date = "2025-01-01" 5 | 6 | [vars] 7 | TEST_MODE = "true" 8 | -------------------------------------------------------------------------------- /src/luna/routes/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/internal/utils", "alias": "utils" } 4 | ], 5 | "supported-targets": [ 6 | "js", 7 | "wasm-gc", 8 | "native" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/astra_app/docs/foo.html: -------------------------------------------------------------------------------- 1 |

Foo Page

2 |

This is a raw HTML page served at /foo/

3 | 8 | -------------------------------------------------------------------------------- /src/_bench/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/_bench" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | 10 | // Type aliases 11 | 12 | // Traits 13 | 14 | -------------------------------------------------------------------------------- /examples/sol_app/.gitignore: -------------------------------------------------------------------------------- 1 | .mooncakes 2 | target 3 | node_modules 4 | # Generated by sol generate (types/ is kept for initial build) 5 | app/__gen__ 6 | app/client/__gen__* 7 | .sol 8 | package-lock.json 9 | wrangler.json 10 | zap-reports -------------------------------------------------------------------------------- /src/astra/markdown/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "luna" }, 4 | { "path": "mizchi/luna/astra", "alias": "astra" }, 5 | { "path": "mizchi/markdown", "alias": "md_parser" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/platform/dom/portal/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | "mizchi/luna/luna/signal", 5 | { 6 | "path": "mizchi/js/browser/dom", 7 | "alias": "js_dom" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /demo-src/game_react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')!).render( 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /src/examples/wc/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/examples/wc" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | 10 | // Type aliases 11 | 12 | // Traits 13 | 14 | -------------------------------------------------------------------------------- /examples/astra_app/moon.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example/astra-app", 3 | "version": "0.1.0", 4 | "deps": { 5 | "mizchi/luna": { 6 | "path": "../.." 7 | } 8 | }, 9 | "source": "docs", 10 | "preferred-target": "js" 11 | } 12 | -------------------------------------------------------------------------------- /src/examples/game/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/examples/game" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | 10 | // Type aliases 11 | 12 | // Traits 13 | 14 | -------------------------------------------------------------------------------- /src/examples/spa/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/examples/spa" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | 10 | // Type aliases 11 | 12 | // Traits 13 | 14 | -------------------------------------------------------------------------------- /src/tests/ssr_test_component/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "luna" } 4 | ], 5 | "link": { 6 | "js": { 7 | "exports": ["render"], 8 | "format": "esm" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/astra/astra_worker/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/astra/astra_worker" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | 10 | // Type aliases 11 | 12 | // Traits 13 | 14 | -------------------------------------------------------------------------------- /examples/astra_app/docs/ssr_test_component/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "luna" } 4 | ], 5 | "link": { 6 | "js": { 7 | "exports": ["render"], 8 | "format": "esm" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/examples/hello_luna/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/examples/hello_luna" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | 10 | // Type aliases 11 | 12 | // Traits 13 | 14 | -------------------------------------------------------------------------------- /src/sol/middleware/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { "path": "mizchi/npm_typed/hono", "alias": "hono" }, 7 | { "path": "mizchi/js/core", "alias": "js" } 8 | ], 9 | "warn-list": "-6" 10 | } 11 | -------------------------------------------------------------------------------- /examples/ssg_test/sol.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssg": { 3 | "docs": "docs", 4 | "output": "dist", 5 | "title": "Shiki Test", 6 | "base": "/", 7 | "sidebar": "auto", 8 | "theme": { 9 | "primary_color": "#3451b2" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /js/luna/examples_preact/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import preact from "@preact/preset-vite"; 3 | 4 | export default defineConfig({ 5 | plugins: [preact()], 6 | root: './', 7 | build: { 8 | outDir: 'dist', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/examples/browser_router/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/examples/browser_router" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | 10 | // Type aliases 11 | 12 | // Traits 13 | 14 | -------------------------------------------------------------------------------- /src/platform/js/cache/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/core/env", "alias": "env" }, 5 | { "path": "mizchi/luna/platform/js/fs_adapter", "alias": "fs_adapter" } 6 | ], 7 | "warn-list": "-29" 8 | } 9 | -------------------------------------------------------------------------------- /src/astra/astra_worker/main.mbt: -------------------------------------------------------------------------------- 1 | // Astra Worker Entry Point 2 | // 3 | // This is the main entry point for worker processes. 4 | // It's forked by the BuilderPool to handle parallel page generation. 5 | 6 | ///| 7 | fn main { 8 | @builder_pool.start_worker() 9 | } 10 | -------------------------------------------------------------------------------- /src/internal/node_fs_adapter/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/core/env", "alias": "env" }, 5 | { "path": "mizchi/js/core", "alias": "js" }, 6 | { "path": "mizchi/js/node/fs", "alias": "fs" } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/astra/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" }, 5 | { "path": "mizchi/luna/internal/json_utils", "alias": "json_utils" }, 6 | { "path": "mizchi/luna/core/ssg", "alias": "ssg" } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/astra/astra_worker/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | { "path": "mizchi/luna/astra/builder_pool", "alias": "builder_pool" } 6 | ], 7 | "link": { 8 | "js": { 9 | "format": "esm" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tests/router_csr/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/tests/router_csr" 3 | 4 | // Values 5 | pub fn mount_router_app() -> Unit 6 | 7 | // Errors 8 | 9 | // Types and methods 10 | 11 | // Type aliases 12 | 13 | // Traits 14 | 15 | -------------------------------------------------------------------------------- /examples/sol_app/moon.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example/sol-app", 3 | "version": "0.1.0", 4 | "deps": { 5 | "mizchi/luna": { 6 | "path": "../.." 7 | }, 8 | "mizchi/js": "0.10.0", 9 | "mizchi/npm_typed": "0.1.1" 10 | }, 11 | "source": "app", 12 | "preferred-target": "js" 13 | } -------------------------------------------------------------------------------- /js/luna/examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "strict": true, 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "../dist", 9 | "noEmit": true, 10 | "skipLibCheck": true 11 | } 12 | } -------------------------------------------------------------------------------- /src/astra/tree/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/astra", "alias": "astra" }, 4 | { "path": "mizchi/luna/core/env", "alias": "env" }, 5 | { "path": "mizchi/luna/core/ssg", "alias": "ssg" }, 6 | { "path": "mizchi/luna/platform/js/fs_adapter", "alias": "fs_adapter" } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/core/routes/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | "mizchi/luna/internal/json_utils", 4 | "mizchi/luna/core/env", 5 | "mizchi/luna/core/ssg" 6 | ], 7 | "test-import": [ 8 | { "path": "mizchi/luna/platform/js/fs_adapter", "alias": "fs_adapter" } 9 | ], 10 | "supported-targets": ["js"] 11 | } 12 | -------------------------------------------------------------------------------- /examples/sol_app/app/server/_using.mbt: -------------------------------------------------------------------------------- 1 | ///| Server-side element DSL imports 2 | using @element { 3 | a, 4 | div, 5 | span, 6 | button, 7 | h1, 8 | h2, 9 | h3, 10 | h4, 11 | p, 12 | nav, 13 | text, 14 | sol_link, 15 | outlet, 16 | form, 17 | input, 18 | label, 19 | ul, 20 | li, 21 | } 22 | -------------------------------------------------------------------------------- /js/luna/examples_preact/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "strict": true, 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "preact", 9 | "noEmit": true, 10 | "skipLibCheck": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /js/luna/examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/platform/js/fs_adapter/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/js/core", "alias": "js" }, 5 | { "path": "mizchi/js/node/fs", "alias": "fs" }, 6 | { "path": "mizchi/npm_typed/memfs", "alias": "memfs" }, 7 | { "path": "mizchi/luna/core/env", "alias": "env" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/platform/dom/element/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | "mizchi/luna/luna/signal", 7 | { 8 | "path": "mizchi/js/core", 9 | "alias": "js" 10 | }, 11 | { 12 | "path": "mizchi/js/browser/dom", 13 | "alias": "js_dom" 14 | } 15 | ], 16 | "warn-list": "-5" 17 | } -------------------------------------------------------------------------------- /src/tests/ssr_test_component/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/tests/ssr_test_component" 3 | 4 | import( 5 | "mizchi/luna/luna" 6 | ) 7 | 8 | // Values 9 | pub fn render() -> @luna.Node[Unit] 10 | 11 | // Errors 12 | 13 | // Types and methods 14 | 15 | // Type aliases 16 | 17 | // Traits 18 | 19 | -------------------------------------------------------------------------------- /demo-src/game_react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React Benchmark Game 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/core/parser/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { 4 | "path": "moonbitlang/parser/lexer", 5 | "alias": "lexer" 6 | }, 7 | { 8 | "path": "moonbitlang/parser/mbti_parser", 9 | "alias": "mbti_parser" 10 | }, 11 | { 12 | "path": "moonbitlang/parser/basic", 13 | "alias": "basic" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/tests/e2e_server/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/tests/e2e_server" 3 | 4 | import( 5 | "mizchi/npm_typed/hono" 6 | ) 7 | 8 | // Values 9 | pub async fn create_app() -> @hono.Hono[Unit, Unit] 10 | 11 | // Errors 12 | 13 | // Types and methods 14 | 15 | // Type aliases 16 | 17 | // Traits 18 | 19 | -------------------------------------------------------------------------------- /js/luna/examples_preact/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Preact + Signals Example 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/platform/js/stream_renderer/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" }, 5 | { "path": "mizchi/luna/luna/render", "alias": "render" }, 6 | { "path": "mizchi/luna/internal/utils", "alias": "utils" }, 7 | { "path": "mizchi/js/web/streams", "alias": "streams" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/examples/hello_luna/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/luna/platform/dom/element", "alias": "dom" }, 7 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" } 8 | ], 9 | "link": { 10 | "js": { 11 | "format": "esm" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/sol/action/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { "path": "mizchi/npm_typed/hono", "alias": "hono" }, 7 | { "path": "mizchi/js/core", "alias": "js" }, 8 | { "path": "mizchi/js/web/http", "alias": "http" }, 9 | { "path": "mizchi/luna/sol/middleware", "alias": "middleware" } 10 | ], 11 | "warn-list": "-6" 12 | } 13 | -------------------------------------------------------------------------------- /src/tests/counter_client/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/tests/counter_client" 3 | 4 | import( 5 | "mizchi/js/browser/dom" 6 | "mizchi/js/core" 7 | ) 8 | 9 | // Values 10 | pub fn hydrate(@dom.Element, @core.Any) -> Unit 11 | 12 | // Errors 13 | 14 | // Types and methods 15 | 16 | // Type aliases 17 | 18 | // Traits 19 | 20 | -------------------------------------------------------------------------------- /examples/sol_app/app/__gen__/types/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" } 5 | ], 6 | "link": { 7 | "js": { 8 | "exports": ["CounterProps", "counter", "ContactFormProps", "contact_form", "WcCounterProps", "wc_counter"], 9 | "format": "esm" 10 | } 11 | }, 12 | "warn-list": "-29" 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | node_modules/ 3 | .mooncakes/ 4 | .DS_Store 5 | *.log 6 | pnpm-lock.yaml 7 | dist 8 | *.coverage 9 | *.0x 10 | test-results/ 11 | wrangler.json 12 | .metrics.db 13 | coverage/ 14 | docs/private-* 15 | .wrangler 16 | __screenshots__ 17 | dist-ssg 18 | app 19 | .sol 20 | docs/_hmr_test_temp.md 21 | docs/public/ 22 | luna-mbt-example 23 | luna-ts-example 24 | dist-docs 25 | .test-output -------------------------------------------------------------------------------- /docs/00_introduction/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | --- 4 | 5 | # Introduction 6 | 7 | Welcome to Luna - a suite of tools for building modern web applications with MoonBit and JavaScript. 8 | 9 | ## Sections 10 | 11 | - [Overview](./overview/) - What is Luna and why use it 12 | - [Getting Started](./getting-started/) - Quick start guide 13 | - [FAQ](./faq/) - Frequently asked questions 14 | -------------------------------------------------------------------------------- /js/luna/examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples-luna", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@luna_ui/luna": "workspace:*" 12 | }, 13 | "devDependencies": { 14 | "typescript": "^5.7.2", 15 | "vite": "^6.0.6" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/examples/todomvc/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/examples/todomvc" 3 | 4 | import( 5 | "moonbitlang/core/json" 6 | ) 7 | 8 | // Values 9 | 10 | // Errors 11 | 12 | // Types and methods 13 | type Todo 14 | pub impl Eq for Todo 15 | pub impl ToJson for Todo 16 | pub impl @json.FromJson for Todo 17 | 18 | // Type aliases 19 | 20 | // Traits 21 | 22 | -------------------------------------------------------------------------------- /js/astra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "strict": true, 7 | "noEmit": true, 8 | "skipLibCheck": true, 9 | "paths": { 10 | "@luna_ui/astra": ["./src/index.ts"] 11 | } 12 | }, 13 | "include": [ 14 | "src/**/*.ts" 15 | ], 16 | "exclude": [ 17 | "src/cli.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/luna/render/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "luna" }, 4 | { "path": "mizchi/luna/internal/utils", "alias": "utils" } 5 | ], 6 | "supported-targets": [ 7 | "js", 8 | "wasm-gc", 9 | "native" 10 | ], 11 | "targets": { 12 | "escape_js.mbt": ["js"], 13 | "escape_non_js.mbt": ["not", "js"], 14 | "escape_bench.mbt": ["js"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/astra_app/astra.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../schemas/astra.schema.json", 3 | "title": "Astra Demo", 4 | "description": "Demo site for Unified Progressive Architecture", 5 | "docs_dir": "docs", 6 | "out_dir": "dist", 7 | "base_url": "/", 8 | "trailing_slash": true, 9 | "deploy": "cloudflare", 10 | "islands": { 11 | "dir": "docs/public/islands", 12 | "basePath": "/islands/" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/internal/node_fs_adapter/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/internal/node_fs_adapter" 3 | 4 | import( 5 | "mizchi/luna/core/env" 6 | ) 7 | 8 | // Values 9 | 10 | // Errors 11 | 12 | // Types and methods 13 | pub struct NodeFS { 14 | } 15 | pub fn NodeFS::new() -> Self 16 | pub impl @env.FileSystem for NodeFS 17 | 18 | // Type aliases 19 | 20 | // Traits 21 | 22 | -------------------------------------------------------------------------------- /js/astra/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | "src/index.ts", 6 | "src/cli.ts", 7 | "src/worker.ts", 8 | ], 9 | format: ["esm"], 10 | dts: true, 11 | outDir: "dist", 12 | clean: true, 13 | minify: true, 14 | outExtensions() { 15 | return { js: ".js", dts: ".d.ts" }; 16 | }, 17 | noExternal: [/\.\.\/\.\.\/\.\.\/target/], 18 | }); 19 | -------------------------------------------------------------------------------- /src/tests/counter_component/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { 4 | "path": "mizchi/luna/luna", 5 | "alias": "luna" 6 | }, 7 | "mizchi/luna/luna/signal", 8 | { "path": "mizchi/luna/luna/render", "alias": "render" } 9 | ], 10 | "link": { 11 | "js": { 12 | "exports": [ 13 | "render_counter_html", 14 | "serialize_state" 15 | ], 16 | "format": "esm" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/examples/game/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/luna/platform/dom/element", "alias": "dom" }, 7 | { "path": "mizchi/js/core", "alias": "js" }, 8 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 9 | "mizchi/js/builtins/global" 10 | ], 11 | "link": { 12 | "js": { 13 | "format": "esm" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tests/e2e_server/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | "mizchi/js/web/http", 4 | "mizchi/npm_typed/hono", 5 | { "path": "mizchi/luna/sol", "alias": "sol" }, 6 | { "path": "mizchi/luna/luna", "alias": "luna" }, 7 | { "path": "mizchi/luna/platform/js/stream_renderer", "alias": "stream_renderer" } 8 | ], 9 | "link": { 10 | "js": { 11 | "exports": [ 12 | "create_app" 13 | ], 14 | "format": "esm" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /js/luna/examples_preact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples-preact", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "preact": "^10.25.4", 12 | "@preact/signals": "^1.3.1" 13 | }, 14 | "devDependencies": { 15 | "@preact/preset-vite": "^2.9.4", 16 | "typescript": "^5.7.2", 17 | "vite": "^6.0.6" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/astra/builder_pool/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/js/core", "alias": "core" }, 5 | { "path": "mizchi/js/node/child_process", "alias": "child_process" }, 6 | { "path": "mizchi/luna/core/ssg", "alias": "ssg" }, 7 | { "path": "mizchi/luna/astra", "alias": "astra" }, 8 | { "path": "mizchi/luna/astra/generator", "alias": "generator" }, 9 | { "path": "mizchi/luna/astra/shiki", "alias": "shiki" } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /docs/ja/04_stella/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Stella 3 | --- 4 | 5 | # Stella 6 | 7 | > **開発中**: Stella は現在開発中です。 8 | 9 | Stella は Luna UI のビジュアル開発環境です。 10 | 11 | ## 予定している機能 12 | 13 | - ビジュアルコンポーネントエディタ 14 | - ホットリロード付きライブプレビュー 15 | - コンポーネントライブラリブラウザ 16 | - デザイントークン管理 17 | 18 | ## ステータス 19 | 20 | Stella がプレビューリリースに達したら、ドキュメントが追加されます。 21 | 22 | ## 関連項目 23 | 24 | - [Luna UI ガイド](/ja/luna/) - コアリアクティビティの概念 25 | - [Astra SSG](/ja/astra/) - 静的サイト生成 26 | - [Sol Framework](/ja/sol/) - フルスタック SSR フレームワーク 27 | -------------------------------------------------------------------------------- /examples/astra_app/docs/posts/hello-world/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello World (Static) 3 | description: This is the static version of Hello World page 4 | --- 5 | 6 | # Hello World - Static Page 7 | 8 | This is a **static page** that should take priority over the dynamically generated `_slug_` version. 9 | 10 | ## Priority Rule 11 | 12 | When both exist: 13 | - `posts/hello-world/index.md` (static) 14 | - `posts/_slug_/index.md` with `{ "slug": "hello-world" }` (dynamic) 15 | 16 | The static page should be used. 17 | -------------------------------------------------------------------------------- /examples/astra_app/docs/tsx_demo/tsx_demo.tsx: -------------------------------------------------------------------------------- 1 | // TSX Demo Component - demonstrates TSX SSR in Astra 2 | 3 | export default function TsxDemo() { 4 | return ( 5 |
6 |

TSX Demo Component

7 |

This component is rendered from a .tsx file!

8 | 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /examples/ssg_test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ssg-test", 3 | "version": "0.1.0", 4 | "type": "module", 5 | "scripts": { 6 | "ssg:build": "node ../../target/js/release/build/sol/cli/cli.js ssg build && npx tsx ../../scripts/shiki-highlight.ts dist", 7 | "ssg:build:only": "node ../../target/js/release/build/sol/cli/cli.js ssg build", 8 | "shiki": "npx tsx ../../scripts/shiki-highlight.ts dist" 9 | }, 10 | "devDependencies": { 11 | "shiki": "^3.0.0", 12 | "tsx": "^4.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/examples/wc/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/luna/luna", "alias": "luna" }, 7 | { "path": "mizchi/luna/platform/js/api", "alias": "luna_api" }, 8 | { "path": "mizchi/js/core", "alias": "js" }, 9 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 10 | "mizchi/js/builtins/global" 11 | ], 12 | "link": { 13 | "js": { 14 | "format": "esm" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vitest.config.cloudflare.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | // Note: @cloudflare/vitest-pool-workers requires vitest 2.x-3.x 4 | // For now, we run routing logic tests without the Workers runtime. 5 | // When actual worker code testing is needed (Phase 6 _worker.js), 6 | // consider using a separate vitest 3.x installation or wrangler dev --test. 7 | 8 | export default defineConfig({ 9 | test: { 10 | include: ["tests/cloudflare/**/*.test.ts"], 11 | reporters: ["dot"], 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /src/examples/hello_luna/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello Luna 7 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/tests/counter_client/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" }, 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 7 | { "path": "mizchi/luna/platform/dom/client", "alias": "client" }, 8 | { "path": "mizchi/js/core", "alias": "js" } 9 | ], 10 | "link": { 11 | "js": { 12 | "exports": [ 13 | "hydrate" 14 | ], 15 | "format": "esm" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/examples/todomvc/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | { "path": "mizchi/luna", "alias": "luna" }, 6 | { "path": "mizchi/luna/platform/dom/element", "alias": "element" }, 7 | { "path": "mizchi/js/browser/dom", "alias": "dom" }, 8 | { "path": "mizchi/js/browser/storage", "alias": "storage" }, 9 | { "path": "mizchi/js/browser/location", "alias": "location" } 10 | ], 11 | "link": { 12 | "js": { 13 | "format": "esm" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/examples/spa/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/luna/platform/dom/element", "alias": "dom" }, 7 | { "path": "mizchi/luna/platform/dom/portal", "alias": "portal" }, 8 | { "path": "mizchi/js/core", "alias": "js" }, 9 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 10 | "mizchi/js/builtins/global" 11 | ], 12 | "link": { 13 | "js": { 14 | "format": "esm" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/sol_app/app/server/about.mbt: -------------------------------------------------------------------------------- 1 | ///| About Page Server Component 2 | 3 | ///| 4 | 5 | ///| Static page without client-side hydration. 6 | 7 | ///| 8 | /// About page - static content (sync server component) 9 | async fn about(props : @router.PageProps) -> @server_dom.ServerNode { 10 | let content = [ 11 | h1([text("About")]), 12 | p([text("Built with MoonBit and Sol framework.")]), 13 | ] 14 | 15 | // Return content only - layout is applied by SolRoutes::Layout 16 | @server_dom.ServerNode::sync(@luna.fragment(content)) 17 | } 18 | -------------------------------------------------------------------------------- /js/luna/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "strict": true, 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "@luna_ui/luna", 9 | "noEmit": true, 10 | "skipLibCheck": true, 11 | "paths": { 12 | "@luna_ui/luna": ["./src/index.ts"], 13 | "@luna_ui/luna/jsx-runtime": ["./src/jsx-runtime.ts"] 14 | } 15 | }, 16 | "include": [ 17 | "src/**/*.ts", 18 | "tests/**/*.ts", 19 | "tests/**/*.tsx" 20 | ] 21 | } -------------------------------------------------------------------------------- /examples/sol_app/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | testDir: './tests', 5 | timeout: 30000, 6 | use: { 7 | baseURL: 'http://localhost:3000', 8 | headless: true, 9 | }, 10 | webServer: { 11 | command: 'cd ../.. && moon build --target js && cd examples/sol_app && node ../../target/js/release/build/sol/cli/cli.js build && node .sol/prod/server/main.js', 12 | port: 3000, 13 | reuseExistingServer: !process.env.CI, 14 | timeout: 120000, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /demo-src/game_react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "game-react", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.2.0", 17 | "@types/react-dom": "^18.2.0", 18 | "@vitejs/plugin-react": "^4.2.0", 19 | "typescript": "^5.3.0", 20 | "vite": "^5.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/tests/counter_component/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/tests/counter_component" 3 | 4 | import( 5 | "mizchi/luna/luna" 6 | "mizchi/luna/luna/signal" 7 | ) 8 | 9 | // Values 10 | pub fn render_counter(@signal.Signal[Int], @luna.EventHandler[Unit], @luna.EventHandler[Unit]) -> @luna.Node[Unit] 11 | 12 | pub fn render_counter_html(Int) -> String 13 | 14 | pub fn serialize_state(Int) -> String 15 | 16 | // Errors 17 | 18 | // Types and methods 19 | 20 | // Type aliases 21 | 22 | // Traits 23 | 24 | -------------------------------------------------------------------------------- /e2e/astra/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | testDir: ".", 5 | testMatch: ["**/*.test.ts"], 6 | fullyParallel: false, // Run tests sequentially since they share the dev server 7 | forbidOnly: !!process.env.CI, 8 | retries: process.env.CI ? 1 : 0, 9 | workers: 1, // Single worker to avoid conflicts 10 | reporter: "dot", 11 | timeout: 60000, // 60 seconds per test 12 | use: { 13 | trace: "on-first-retry", 14 | }, 15 | // No webServer - tests manage their own dev server 16 | }); 17 | -------------------------------------------------------------------------------- /examples/sol_app/app/client/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/platform/dom/element", "alias": "element" }, 5 | { "path": "mizchi/luna/luna/signal", "alias": "signal" }, 6 | { "path": "mizchi/luna/sol/action", "alias": "action" }, 7 | { "path": "mizchi/js/core", "alias": "js" }, 8 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" } 9 | ], 10 | "link": { 11 | "js": { 12 | "exports": ["contact_form", "counter", "wc_counter"], 13 | "format": "esm" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /js/luna/tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsdown"; 2 | 3 | export default defineConfig({ 4 | entry: [ 5 | "src/index.ts", 6 | "src/jsx-runtime.ts", 7 | "src/jsx-dev-runtime.ts", 8 | ], 9 | format: ["esm"], 10 | dts: true, 11 | outDir: "dist", 12 | clean: true, 13 | minify: true, 14 | // Use .js extension for ESM (package.json has "type": "module") 15 | outExtensions() { 16 | return { js: ".js", dts: ".d.ts" }; 17 | }, 18 | // Bundle the MoonBit output into the package 19 | noExternal: [/\.\.\/\.\.\/\.\.\/target/], 20 | }); 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "strict": true, 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "@luna_ui/luna", 9 | "noEmit": true, 10 | "skipLibCheck": true, 11 | "baseUrl": "." 12 | }, 13 | "include": [ 14 | "js/**/*.ts", 15 | "js/**/*.tsx" 16 | ], 17 | "exclude": [ 18 | "js/wcssr/**", 19 | "js/astra/src/cli.ts", 20 | "js/luna/tests/**", 21 | "js/luna/examples/**", 22 | "js/luna/examples_preact/**" 23 | ] 24 | } -------------------------------------------------------------------------------- /js/wcssr/vitest.bench.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | import { playwright } from '@vitest/browser-playwright'; 3 | 4 | export default defineConfig({ 5 | test: { 6 | browser: { 7 | enabled: true, 8 | headless: true, 9 | provider: playwright(), 10 | instances: [{ browser: 'chromium' }], 11 | }, 12 | include: ['tests/*.bench.ts', 'tests/bench.browser.test.ts'], 13 | benchmark: { 14 | include: ['tests/*.bench.ts', 'tests/bench.browser.test.ts'], 15 | reporters: ['default'], 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /src/tests/router_csr/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna/routes", "alias": "routes" }, 5 | { "path": "mizchi/luna/platform/dom/router", "alias": "browser_router" }, 6 | { "path": "mizchi/luna/platform/dom/element", "alias": "dom" }, 7 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 8 | { "path": "mizchi/js/core", "alias": "js" } 9 | ], 10 | "link": { 11 | "js": { 12 | "exports": [ 13 | "mount_router_app" 14 | ], 15 | "format": "esm" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/examples/todomvc/storage.mbt: -------------------------------------------------------------------------------- 1 | // TodoMVC - LocalStorage Persistence 2 | 3 | ///| 4 | let storage_key : String = "todos-luna" 5 | 6 | ///| 7 | fn save_todos(todos : Array[Todo]) -> Unit { 8 | @storage.localStorage().setItem(storage_key, todos.to_json().stringify()) 9 | } 10 | 11 | ///| 12 | fn load_todos() -> Array[Todo] { 13 | match @storage.localStorage().getItem(storage_key) { 14 | Some(str) => { 15 | let json = @json.parse(str) catch { _ => return [] } 16 | @json.from_json(json) catch { 17 | _ => [] 18 | } 19 | } 20 | None => [] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/examples/wiki/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/luna/luna/routes", "alias": "routes" }, 7 | { "path": "mizchi/luna/platform/dom/router", "alias": "router" }, 8 | { "path": "mizchi/luna/platform/dom/element", "alias": "dom" }, 9 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 10 | { "path": "mizchi/js/core", "alias": "js" } 11 | ], 12 | "link": { 13 | "js": { 14 | "format": "esm", 15 | "exports": ["hydrate"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/stella/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "luna" }, 4 | { "path": "mizchi/luna/luna/render", "alias": "render" } 5 | ], 6 | "link": { 7 | "js": { 8 | "exports": [ 9 | "generate_shard", 10 | "generate_shard_with_state_script", 11 | "generate_minimal_shard", 12 | "generate_standalone_shard", 13 | "generate_lazy_shard", 14 | "generate_shard_group", 15 | "escape_json_for_html", 16 | "generate_state_script" 17 | ], 18 | "format": "esm" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /js/luna/examples/app.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal, createMemo, render } from '@mizchi/luna'; 2 | 3 | function Counter() { 4 | const [count, setCount] = createSignal(0); 5 | const doubled = createMemo(() => count() * 2); 6 | 7 | return ( 8 |
9 |

Count: {count}

10 |

Doubled: {doubled}

11 | 12 | 13 | 14 |
15 | ); 16 | } 17 | 18 | render(document.getElementById('app')!, ); 19 | -------------------------------------------------------------------------------- /src/examples/browser_router/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": ["js"], 4 | "import": [ 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/luna/luna/routes", "alias": "routes" }, 7 | { "path": "mizchi/luna/platform/dom/router", "alias": "router" }, 8 | { "path": "mizchi/luna/platform/dom/element", "alias": "dom" }, 9 | { "path": "mizchi/js/core", "alias": "js" }, 10 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 11 | "mizchi/js/builtins/global" 12 | ], 13 | "link": { 14 | "js": { 15 | "format": "esm" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /js/luna/examples_preact/app.tsx: -------------------------------------------------------------------------------- 1 | import { signal, computed } from '@preact/signals'; 2 | import { render } from 'preact'; 3 | 4 | function Counter() { 5 | const count = signal(0); 6 | const doubled = computed(() => count.value * 2); 7 | 8 | return ( 9 |
10 |

Count: {count}

11 |

Doubled: {doubled}

12 | 13 | 14 | 15 |
16 | ); 17 | } 18 | 19 | render(, document.getElementById('app')!); 20 | -------------------------------------------------------------------------------- /src/sol/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { 7 | "path": "mizchi/luna/luna", 8 | "alias": "luna" 9 | }, 10 | { "path": "mizchi/luna/luna/render", "alias": "render" }, 11 | { "path": "mizchi/luna/platform/js/stream_renderer", "alias": "stream_renderer" }, 12 | "mizchi/luna/stella", 13 | { "path": "mizchi/js/core", "alias": "js" }, 14 | { 15 | "path": "mizchi/npm_typed/hono", 16 | "alias": "hono" 17 | }, 18 | { 19 | "path": "mizchi/js/web/http", 20 | "alias": "http" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /demo-src/game_react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "moduleResolution": "bundler", 9 | "allowImportingTsExtensions": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "noEmit": true, 13 | "jsx": "react-jsx", 14 | "strict": true, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": true, 17 | "noFallthroughCasesInSwitch": true 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /src/astra/routes/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/astra", "alias": "astra" }, 5 | { "path": "mizchi/luna/astra/markdown", "alias": "markdown" }, 6 | { "path": "mizchi/luna/luna", "alias": "luna" }, 7 | { "path": "mizchi/luna/core/ssg", "alias": "ssg" }, 8 | { "path": "mizchi/luna/core/env", "alias": "env" }, 9 | { "path": "mizchi/luna/core/routes", "alias": "core_routes" }, 10 | { "path": "mizchi/luna/platform/js/fs_adapter", "alias": "fs_adapter" }, 11 | { "path": "mizchi/luna/internal/json_utils", "alias": "json_utils" } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /e2e/browser-router/browser-router.test.ts-snapshots/users-page-layout.txt: -------------------------------------------------------------------------------- 1 |

Users Section

This is a layout wrapper for all /users/* routes

User List

  • User 1 - Alice
  • User 2 - Bob
  • User 3 - Charlie
-------------------------------------------------------------------------------- /docs/04_stella/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Stella 3 | --- 4 | 5 | # Stella 6 | 7 | > **Coming Soon**: Stella is under development. 8 | 9 | Stella is a visual development environment for Luna UI. 10 | 11 | ## Planned Features 12 | 13 | - Visual component editor 14 | - Live preview with hot reload 15 | - Component library browser 16 | - Design token management 17 | 18 | ## Status 19 | 20 | Documentation will be added when Stella reaches preview release. 21 | 22 | ## See Also 23 | 24 | - [Luna UI Guide](/luna/) - Core reactivity concepts 25 | - [Astra SSG](/astra/) - Static site generation 26 | - [Sol Framework](/sol/) - Full-stack SSR framework 27 | -------------------------------------------------------------------------------- /src/astra/docs/ja/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Luna SSG 3 | description: MoonBitで作られた静的サイトジェネレーター 4 | layout: home 5 | --- 6 | 7 | # Luna SSG 8 | 9 | MoonBit で実装された静的サイトジェネレーター。VitePress/Docusaurus 風のドキュメントサイトを生成できます。 10 | 11 | ## 特徴 12 | 13 | - **ファイルベースルーティング** - `docs/` 配下のMarkdownから自動ルート生成 14 | - **Frontmatter対応** - YAMLでページメタデータを定義 15 | - **VitePress風テーマ** - ナビ、サイドバー、目次を自動生成 16 | - **多言語対応** - 複数言語のドキュメントをサポート 17 | - **Island Architecture** - 部分的Hydration対応(将来) 18 | 19 | ## クイックスタート 20 | 21 | ```bash 22 | # docsディレクトリを作成 23 | mkdir docs 24 | 25 | # Markdownファイルを追加 26 | echo "# Hello World" > docs/index.md 27 | 28 | # ビルド実行 29 | sol ssg build 30 | ``` 31 | -------------------------------------------------------------------------------- /src/platform/dom/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | "mizchi/luna/luna/signal", 7 | { 8 | "path": "mizchi/luna/luna", 9 | "alias": "luna" 10 | }, 11 | { 12 | "path": "mizchi/luna/platform/dom/client", 13 | "alias": "client" 14 | }, 15 | { 16 | "path": "mizchi/luna/platform/dom/element", 17 | "alias": "element" 18 | }, 19 | { "path": "mizchi/js/core", "alias": "js" }, 20 | { 21 | "path": "mizchi/js/browser/dom", 22 | "alias": "js_dom" 23 | }, 24 | "mizchi/js/builtins/global" 25 | ], 26 | "test-import": [] 27 | } 28 | -------------------------------------------------------------------------------- /js/wcssr/src/server-entry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Web Components SSR - Server Entry 3 | * 4 | * サーバーサイド専用。DOM依存なし。 5 | * Node.js / Deno / Bun / MoonBit(WASM) で動作可能。 6 | */ 7 | 8 | // Types (サーバーで必要なもののみ) 9 | export type { 10 | State, 11 | ComponentDef, 12 | SSROptions, 13 | RenderOptions, 14 | CSSStrategy, 15 | RenderFn, 16 | } from './types.js'; 17 | 18 | // Server functions 19 | export { 20 | escapeAttr, 21 | escapeHtml, 22 | escapeJson, 23 | createSSRRenderer, 24 | renderDocument, 25 | renderComponentInline, 26 | renderComponentLink, 27 | } from './server.js'; 28 | 29 | // Re-export DocumentOptions type 30 | export type { DocumentOptions } from './server.js'; 31 | -------------------------------------------------------------------------------- /src/internal/utils/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/internal/utils" 3 | 4 | // Values 5 | pub fn extract_substring(String, Int, Int) -> String 6 | 7 | pub fn extract_until(String, Array[Char]) -> String 8 | 9 | pub fn extract_wc_name_from_url(String) -> String 10 | 11 | pub fn slice_from(String, Int) -> String 12 | 13 | pub fn slice_to(String, Int) -> String 14 | 15 | pub fn slice_view_from(StringView, Int) -> String 16 | 17 | pub fn slice_view_to(StringView, Int) -> String 18 | 19 | pub fn split_by(String, Char) -> Array[String] 20 | 21 | // Errors 22 | 23 | // Types and methods 24 | 25 | // Type aliases 26 | 27 | // Traits 28 | 29 | -------------------------------------------------------------------------------- /examples/sol_app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sol-app-example", 3 | "version": "0.1.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "node ../../target/js/release/build/sol/cli/cli.js dev", 7 | "build": "node ../../target/js/release/build/sol/cli/cli.js build", 8 | "serve": "node ../../target/js/release/build/sol/cli/cli.js serve", 9 | "test": "playwright test", 10 | "test:e2e": "playwright test tests/e2e.test.ts" 11 | }, 12 | "dependencies": { 13 | "@hono/node-server": "^1.0.0", 14 | "hono": "^4.0.0" 15 | }, 16 | "devDependencies": { 17 | "@playwright/test": "^1.57.0", 18 | "rolldown": "^1.0.0-beta.54", 19 | "wrangler": "^4.54.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /moon.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mizchi/luna", 3 | "version": "0.1.5", 4 | "deps": { 5 | "moonbitlang/async": "0.14.3", 6 | "moonbitlang/x": "0.4.38", 7 | "mizchi/npm_typed": "0.1.5", 8 | "moonbitlang/parser": "0.1.12", 9 | "mizchi/markdown": "0.1.3", 10 | "mizchi/js": "0.10.6", 11 | "mizchi/jsonschema": "0.6.0" 12 | }, 13 | "readme": "README.md", 14 | "repository": "https://github.com/mizchi/luna", 15 | "license": "MIT", 16 | "keywords": [ 17 | "luna", 18 | "ui", 19 | "signals" 20 | ], 21 | "description": "Fine-grained reactive UI library for Moonbit/JS", 22 | "source": "src", 23 | "warn-list": "-unused_trait_bound", 24 | "preferred-target": "js" 25 | } -------------------------------------------------------------------------------- /src/platform/dom/client/repair/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { 7 | "path": "mizchi/luna/luna", 8 | "alias": "luna" 9 | }, 10 | "mizchi/luna/luna/signal", 11 | { "path": "mizchi/js/core", "alias": "js" }, 12 | { 13 | "path": "mizchi/js/browser/dom", 14 | "alias": "js_dom" 15 | }, 16 | "mizchi/js/web/event", 17 | "mizchi/js/builtins/global", 18 | { 19 | "path": "mizchi/luna/platform/dom/client", 20 | "alias": "client" 21 | } 22 | ], 23 | "test-import": [ 24 | "mizchi/npm_typed/global_jsdom", 25 | { "path": "mizchi/luna/luna/render", "alias": "render" } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /src/astra/sol.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssg": { 3 | "docs": "docs", 4 | "output": "dist", 5 | "title": "🌗 Luna SSG", 6 | "description": "MoonBit製の静的サイトジェネレーター", 7 | "base": "/", 8 | "nav": [ 9 | { "text": "Guide", "link": "/guide/" } 10 | ], 11 | "sidebar": "auto", 12 | "theme": { 13 | "primaryColor": "#10b981" 14 | }, 15 | "footer": { 16 | "message": "Built with Luna SSG", 17 | "copyright": "2024 mizchi" 18 | }, 19 | "i18n": { 20 | "defaultLocale": "en", 21 | "locales": [ 22 | { "code": "en", "label": "English", "path": "" }, 23 | { "code": "ja", "label": "日本語", "path": "ja" } 24 | ] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/platform/dom/debounced.mbt: -------------------------------------------------------------------------------- 1 | // Debounced Signal - Timer-based signal debouncing (requires JS runtime) 2 | // 3 | 4 | ///| 5 | /// Debounce signal updates using setTimeout 6 | pub fn[T] debounced( 7 | sig : @signal.Signal[T], 8 | delay_ms : Int, 9 | ) -> @signal.Signal[T] { 10 | let result = @signal.Signal::new(sig.peek()) 11 | let timer_id : Ref[@global.Timer?] = { val: None } 12 | let _ = @signal.on(sig, fn(value) { 13 | // Cancel previous timer 14 | match timer_id.val { 15 | Some(id) => @global.clearTimeout(id) 16 | None => () 17 | } 18 | // Set new timer 19 | timer_id.val = Some( 20 | @global.setTimeout(fn() { result.set(value) }, delay_ms), 21 | ) 22 | }) 23 | result 24 | } 25 | -------------------------------------------------------------------------------- /js/wcssr/vitest.browser.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | import { playwright } from '@vitest/browser-playwright'; 3 | 4 | // Browser name from CLI or default to chromium 5 | // Usage: pnpm test:browser -- --browser.name=firefox 6 | const browserName = (process.env.BROWSER || 'chromium') as 'chromium' | 'firefox' | 'webkit'; 7 | 8 | export default defineConfig({ 9 | test: { 10 | browser: { 11 | enabled: true, 12 | headless: true, 13 | provider: playwright(), 14 | instances: [{ browser: browserName }], 15 | }, 16 | include: ['tests/**/*.browser.test.ts'], 17 | // Exclude bench tests from regular test runs 18 | exclude: ['tests/bench.browser.test.ts'], 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /src/platform/dom/client/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { 7 | "path": "mizchi/luna/luna", 8 | "alias": "luna" 9 | }, 10 | "mizchi/luna/luna/signal", 11 | { "path": "mizchi/luna/internal/utils", "alias": "utils" }, 12 | { "path": "mizchi/js/core", "alias": "js" }, 13 | { 14 | "path": "mizchi/js/browser/dom", 15 | "alias": "js_dom" 16 | }, 17 | "mizchi/js/web/event", 18 | "mizchi/js/builtins/global" 19 | ], 20 | "test-import": [ 21 | "mizchi/npm_typed/global_jsdom", 22 | { "path": "mizchi/luna/luna/render", "alias": "render" }, 23 | { "path": "mizchi/luna/platform/dom/element", "alias": "element" } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /examples/astra_app/docs/posts/_slug_/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: A blog post example 3 | --- 4 | 5 | # Post Page 6 | 7 | This is a dynamically generated post page. 8 | 9 | The URL path is determined by the `slug` parameter in `page.json`. 10 | 11 | ## How it works 12 | 13 | 1. Create a `_slug_` directory (parameter name between underscores) 14 | 2. Add `page.json` with `staticParams` array 15 | 3. Add `index.md` as the template 16 | 4. Astra generates a page for each param in `staticParams` 17 | 18 | ## Example 19 | 20 | ```json 21 | { 22 | "staticParams": [ 23 | { "slug": "hello-world" }, 24 | { "slug": "getting-started" } 25 | ] 26 | } 27 | ``` 28 | 29 | This generates: 30 | - `/posts/hello-world/` 31 | - `/posts/getting-started/` 32 | -------------------------------------------------------------------------------- /js/loader/src/router/index.ts: -------------------------------------------------------------------------------- 1 | /*! luna router v1 - Client-side routing module */ 2 | 3 | // Hybrid router (fetch + swap, Turbo/HTMX style) 4 | export { 5 | HybridRouter, 6 | getHybridRouter, 7 | startHybridRouter, 8 | type HybridRouterOptions, 9 | } from './hybrid'; 10 | 11 | // SPA router (client-side rendering) 12 | export { 13 | SpaRouter, 14 | getSpaRouter, 15 | createSpaRouter, 16 | type SpaRouterOptions, 17 | type SpaRouteConfig, 18 | type RouteParams, 19 | } from './spa'; 20 | 21 | // Scroll management 22 | export { 23 | ScrollManager, 24 | getScrollManager, 25 | enableNativeScrollRestoration, 26 | disableNativeScrollRestoration, 27 | type ScrollPosition, 28 | type ScrollManagerOptions, 29 | } from './scroll'; 30 | -------------------------------------------------------------------------------- /src/_bench/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { 7 | "path": "mizchi/luna/luna", 8 | "alias": "luna" 9 | }, 10 | "mizchi/luna/luna/signal", 11 | { 12 | "path": "mizchi/luna/luna/render", 13 | "alias": "ssr" 14 | }, 15 | { 16 | "path": "mizchi/luna/platform/dom/client", 17 | "alias": "client" 18 | }, 19 | { 20 | "path": "mizchi/luna/platform/dom/client/repair", 21 | "alias": "fixable" 22 | }, 23 | { 24 | "path": "mizchi/js/browser/dom", 25 | "alias": "js_dom" 26 | }, 27 | { 28 | "path": "mizchi/js/core", 29 | "alias": "js" 30 | }, 31 | "mizchi/npm_typed/global_jsdom" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /examples/astra_app/docs/wiki/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Wiki 3 | description: Client-side dynamic routing with Luna router 4 | islands: 5 | - wiki 6 | --- 7 | 8 | # Wiki Demo 9 | 10 | This page demonstrates Luna's BrowserRouter for client-side dynamic routing. 11 | 12 | 13 | 14 | ## How it works 15 | 16 | 1. **BrowserRouter**: Handles client-side navigation using History API 17 | 2. **Dynamic Parameters**: Routes like `/wiki/:slug` extract the slug parameter 18 | 3. **Signal-based Updates**: Reactive UI updates when route changes 19 | 20 | ### URL Patterns 21 | 22 | - `/wiki/` - Wiki Home 23 | - `/wiki/getting-started` - Getting Started page 24 | - `/wiki/configuration` - Configuration page 25 | - `/wiki/api-reference` - API Reference page 26 | -------------------------------------------------------------------------------- /js/wcssr/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "declaration": true, 10 | "declarationMap": true, 11 | "sourceMap": true, 12 | "outDir": "./dist", 13 | "rootDir": "./src", 14 | "esModuleInterop": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noImplicitReturns": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "resolveJsonModule": true, 21 | "isolatedModules": true 22 | }, 23 | "include": ["src/**/*"], 24 | "exclude": ["node_modules", "dist"] 25 | } 26 | -------------------------------------------------------------------------------- /src/astra/tree/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/astra/tree" 3 | 4 | import( 5 | "mizchi/luna/astra" 6 | "mizchi/luna/core/env" 7 | "mizchi/luna/core/ssg" 8 | ) 9 | 10 | // Values 11 | pub fn build_document_tree(@astra.SsgConfig, Array[@astra.PageMeta], String) -> @ssg.DocumentTree 12 | 13 | pub fn[F : @env.FileSystem] build_document_tree_with_fs(F, @astra.SsgConfig, Array[@astra.PageMeta], String) -> @ssg.DocumentTree 14 | 15 | pub fn generate_llms_txt(@ssg.DocumentTree) -> String 16 | 17 | pub fn generate_rss(@ssg.DocumentTree, limit? : Int) -> String 18 | 19 | pub fn generate_sitemap(@ssg.DocumentTree) -> String 20 | 21 | // Errors 22 | 23 | // Types and methods 24 | 25 | // Type aliases 26 | 27 | // Traits 28 | 29 | -------------------------------------------------------------------------------- /examples/sol_app/app/client/counter.mbt: -------------------------------------------------------------------------------- 1 | // Counter Client Component 2 | // Client components receive Props and return DomNode. 3 | 4 | ///| 5 | /// Props passed from server 6 | pub(all) struct CounterProps { 7 | initial_count : Int 8 | } derive(ToJson, FromJson) 9 | 10 | ///| 11 | /// Counter component 12 | pub fn counter(props : CounterProps) -> DomNode { 13 | let count = @signal.signal(props.initial_count) 14 | div(class="counter", [ 15 | span(class="count-display", [text_of(count)]), 16 | div(class="buttons", [ 17 | button(class="dec", on=events().click(_ => count.update(n => n - 1)), [ 18 | text("-"), 19 | ]), 20 | button(class="inc", on=events().click(_ => count.update(n => n + 1)), [ 21 | text("+"), 22 | ]), 23 | ]), 24 | ]) 25 | } 26 | -------------------------------------------------------------------------------- /js/astra/src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { fileURLToPath } from "node:url"; 3 | import { dirname, join } from "node:path"; 4 | 5 | // Set assets directory for MoonBit loader 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = dirname(__filename); 8 | 9 | // Assets are at js/astra/assets/ relative to dist/cli.js 10 | // dist/cli.js -> js/astra/dist/cli.js 11 | // assets -> js/astra/assets/ 12 | (globalThis as any).__astra_assets_dir = join(__dirname, "..", "assets"); 13 | 14 | // Worker script is at dist/worker.js relative to dist/cli.js 15 | (globalThis as any).__astra_worker_script = join(__dirname, "worker.js"); 16 | 17 | // Import MoonBit CLI (dynamic import to ensure globalThis is set first) 18 | await import("../../../target/js/release/build/astra/cli/cli"); 19 | -------------------------------------------------------------------------------- /src/core/parser/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/core/parser" 3 | 4 | // Values 5 | pub fn get_methods_for_type(Array[PubFunc], String) -> Array[String] 6 | 7 | pub fn get_props_structs(Array[StructDef]) -> Array[StructDef] 8 | 9 | pub fn get_standalone_funcs(Array[PubFunc]) -> Array[String] 10 | 11 | pub fn parse_pub_funcs(String, String) -> Array[PubFunc] raise 12 | 13 | pub fn parse_struct_defs(String, String) -> Array[StructDef] 14 | 15 | // Errors 16 | 17 | // Types and methods 18 | pub(all) struct PubFunc { 19 | name : String 20 | type_name : String? 21 | } 22 | 23 | pub(all) struct StructDef { 24 | name : String 25 | visibility : String 26 | raw_definition : String 27 | } 28 | 29 | // Type aliases 30 | 31 | // Traits 32 | 33 | -------------------------------------------------------------------------------- /src/examples/hello_luna/main.mbt: -------------------------------------------------------------------------------- 1 | ///| 2 | /// Minimal Luna Counter - for bundle size measurement 3 | /// 4 | /// This is the simplest possible Luna app: 5 | /// - One Signal (count) 6 | /// - One button with click handler 7 | /// - Text that updates reactively 8 | fn main { 9 | let doc = @js_dom.document() 10 | match doc.getElementById("app") { 11 | Some(el) => { 12 | let count = @signal.signal(0) 13 | let app = @dom.div([ 14 | @dom.p([@dom.text_dyn(fn() { "Count: " + count.get().to_string() })]), 15 | @dom.button( 16 | on=@dom.events().click(fn(_) { count.update(fn(n) { n + 1 }) }), 17 | [@dom.text("Click me")], 18 | ), 19 | ]) 20 | @dom.render(el |> @dom.DomElement::from_jsdom, app) 21 | } 22 | None => () 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/internal/utils/wc_name.mbt: -------------------------------------------------------------------------------- 1 | // Web Component Name Utilities 2 | 3 | ///| 4 | /// Extract Web Component name from URL (e.g., /static/wc_counter.js -> wc-counter) 5 | pub fn extract_wc_name_from_url(url : String) -> String { 6 | // Extract filename without extension 7 | let parts = url.split("/").collect() 8 | let filename_view = if parts.length() > 0 { 9 | parts[parts.length() - 1] 10 | } else { 11 | "".view() 12 | } 13 | let filename = filename_view.to_string() 14 | let name = if filename.has_suffix(".js") && filename.length() > 3 { 15 | filename[:filename.length() - 3].to_string() catch { 16 | _ => filename 17 | } 18 | } else { 19 | filename 20 | } 21 | // Convert underscores to hyphens for custom element name 22 | name.replace(old="_", new="-") 23 | } 24 | -------------------------------------------------------------------------------- /experiments/webcomponents_ssr/demo-component.css: -------------------------------------------------------------------------------- 1 | /** 2 | * demo-component.css 3 | * link戦略でロードされる外部CSS 4 | */ 5 | 6 | :host { 7 | display: block; 8 | padding: 12px; 9 | border: 2px solid #2ecc71; 10 | border-radius: 6px; 11 | margin: 8px 0; 12 | background: #f0fff4; 13 | } 14 | 15 | .value { 16 | font-size: 1.5rem; 17 | font-weight: bold; 18 | color: #27ae60; 19 | } 20 | 21 | button { 22 | margin: 4px; 23 | padding: 4px 12px; 24 | border: none; 25 | border-radius: 4px; 26 | cursor: pointer; 27 | transition: transform 0.1s; 28 | } 29 | 30 | button:active { 31 | transform: scale(0.95); 32 | } 33 | 34 | button[data-action="inc"] { 35 | background: #27ae60; 36 | color: white; 37 | } 38 | 39 | button[data-action="dec"] { 40 | background: #e74c3c; 41 | color: white; 42 | } 43 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | import { resolve } from "path"; 3 | 4 | export default defineConfig({ 5 | esbuild: { 6 | jsx: "automatic", 7 | jsxImportSource: resolve(__dirname, "js/luna/src"), 8 | }, 9 | test: { 10 | // Default: Node.js environment with jsdom for DOM tests 11 | environment: "jsdom", 12 | reporters: ["dot"], 13 | include: [ 14 | "js/loader/**/*.test.ts", 15 | "js/luna/tests/**/*.test.tsx", // TSX tests need jsdom (global-jsdom) 16 | "e2e/sol/cli/**/*.test.ts", 17 | "scripts/**/*.test.ts", 18 | "tests/cloudflare/**/*.test.ts", // Cloudflare routing logic tests 19 | ], 20 | exclude: [ 21 | "**/node_modules/**", 22 | "**/.mooncakes/**", 23 | "js/**/tmp/**", 24 | ], 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /src/platform/js/fs_adapter/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/platform/js/fs_adapter" 3 | 4 | import( 5 | "mizchi/luna/core/env" 6 | "mizchi/npm_typed/memfs" 7 | ) 8 | 9 | // Values 10 | 11 | // Errors 12 | 13 | // Types and methods 14 | pub struct MemFSAdapter { 15 | fs : @memfs.MemFS 16 | vol : @memfs.Volume 17 | } 18 | pub fn MemFSAdapter::from_memfs(@memfs.Volume, @memfs.MemFS) -> Self 19 | pub fn MemFSAdapter::memfs(Self) -> @memfs.MemFS 20 | pub fn MemFSAdapter::new() -> Self 21 | pub fn MemFSAdapter::volume(Self) -> @memfs.Volume 22 | pub impl @env.FileSystem for MemFSAdapter 23 | 24 | pub struct NodeFsAdapter { 25 | } 26 | pub fn NodeFsAdapter::new() -> Self 27 | pub impl @env.FileSystem for NodeFsAdapter 28 | 29 | // Type aliases 30 | 31 | // Traits 32 | 33 | -------------------------------------------------------------------------------- /src/astra/cli/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/astra/cli" 3 | 4 | import( 5 | "mizchi/luna/astra" 6 | ) 7 | 8 | // Values 9 | pub fn lint_docs(@astra.SsgConfig, String) -> LintResult 10 | 11 | pub fn run_lint_command(Array[String]) -> Unit 12 | 13 | // Errors 14 | 15 | // Types and methods 16 | type AstraTemplate 17 | 18 | type HmrServer 19 | 20 | pub(all) struct LintIssue { 21 | severity : LintSeverity 22 | file : String 23 | line : Int? 24 | message : String 25 | rule : String 26 | } 27 | 28 | pub(all) struct LintResult { 29 | issues : Array[LintIssue] 30 | files_checked : Int 31 | errors : Int 32 | warnings : Int 33 | } 34 | 35 | pub(all) enum LintSeverity { 36 | Error 37 | Warning 38 | Info 39 | } 40 | 41 | // Type aliases 42 | 43 | // Traits 44 | 45 | -------------------------------------------------------------------------------- /src/astra/docs/ja/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: はじめに 3 | description: Luna SSGの使い方ガイド 4 | --- 5 | 6 | # はじめに 7 | 8 | Luna SSG は Sol CLI に統合された静的サイトジェネレーターです。 9 | 10 | ## インストール 11 | 12 | MoonBit と Node.js がインストールされている必要があります。 13 | 14 | ## ディレクトリ構成 15 | 16 | ``` 17 | project/ 18 | ├── docs/ # デフォルト言語 (英語) 19 | │ └── index.md 20 | ├── docs/ja/ # 日本語 21 | │ └── index.md 22 | ├── sol.config.json 23 | └── dist/ # 出力先 24 | ``` 25 | 26 | ## 多言語対応 27 | 28 | `sol.config.json` で言語を設定します: 29 | 30 | ```json 31 | { 32 | "ssg": { 33 | "i18n": { 34 | "defaultLocale": "en", 35 | "locales": [ 36 | { "code": "en", "label": "English", "path": "" }, 37 | { "code": "ja", "label": "日本語", "path": "ja" } 38 | ] 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | 翻訳がない場合、自動的にデフォルト言語(英語)にフォールバックします。 45 | -------------------------------------------------------------------------------- /js/wcssr/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Web Components SSR - Main Entry 3 | * 4 | * サーバー/クライアント共通のエクスポート 5 | */ 6 | 7 | // Types 8 | export type { 9 | State, 10 | EventPayload, 11 | Handler, 12 | RenderFn, 13 | CSSStrategy, 14 | ComponentDef, 15 | SSROptions, 16 | RenderOptions, 17 | RegisterOptions, 18 | SimpleComponentDef, 19 | HandlerResult, 20 | } from './types.js'; 21 | 22 | // Server (純粋関数、DOM依存なし) 23 | export { 24 | escapeAttr, 25 | escapeHtml, 26 | escapeJson, 27 | createSSRRenderer, 28 | renderDocument, 29 | renderComponentInline, 30 | renderComponentLink, 31 | } from './server.js'; 32 | 33 | // Client (ブラウザ環境のみ) 34 | export { 35 | registerComponent, 36 | registerComponents, 37 | defineComponent, 38 | hydrateElement, 39 | clearStyleSheetCache, 40 | getRegisteredComponents, 41 | } from './client.js'; 42 | -------------------------------------------------------------------------------- /src/core/ssg/renderer_type.mbt: -------------------------------------------------------------------------------- 1 | ///| 2 | /// Renderer type for content rendering 3 | /// Determined during file scanning based on content type and configuration 4 | 5 | ///| 6 | /// Renderer types supported by Astra SSG 7 | pub(all) enum RendererType { 8 | /// Markdown renderer - converts .md to HTML via markdown parser 9 | MarkdownRenderer 10 | /// HTML renderer - passes through .html content (optionally sanitized) 11 | HtmlRenderer 12 | /// Luna renderer - MoonBit components with SSR via compiled modules 13 | LunaRenderer 14 | /// React renderer - TSX components with React SSR 15 | ReactRenderer 16 | /// Client-only renderer - placeholder for client-side hydration 17 | ClientOnlyRenderer 18 | } derive(Eq, Show) 19 | 20 | ///| 21 | /// Default renderer type 22 | pub fn RendererType::default() -> RendererType { 23 | MarkdownRenderer 24 | } 25 | -------------------------------------------------------------------------------- /src/tests/browser_components/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" }, 5 | "mizchi/luna/luna/signal", 6 | { "path": "mizchi/js/browser/dom", "alias": "js_dom" }, 7 | { "path": "mizchi/luna/platform/dom/element", "alias": "dom" }, 8 | { "path": "mizchi/luna/platform/dom/client", "alias": "client" }, 9 | { "path": "mizchi/js/core", "alias": "js" } 10 | ], 11 | "link": { 12 | "js": { 13 | "exports": [ 14 | "hydrate_signal_effect", 15 | "hydrate_dynamic_attrs", 16 | "hydrate_show_toggle", 17 | "hydrate_for_each", 18 | "hydrate_events", 19 | "hydrate_sortable_list", 20 | "hydrate_input_binding", 21 | "hydrate_element_ref" 22 | ], 23 | "format": "esm" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/sol_app/scripts/patch-for-cloudflare.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Cloudflare Workers向けパッチスクリプト 3 | import { readFileSync, writeFileSync } from 'fs'; 4 | 5 | const file = process.argv[2]; 6 | if (!file) { 7 | console.error('Usage: node patch-for-cloudflare.js '); 8 | process.exit(1); 9 | } 10 | 11 | let content = readFileSync(file, 'utf-8'); 12 | 13 | // 1. random_seed をコメントアウトして固定値に置換 14 | content = content.replace( 15 | /^(var moonbitlang\$core\$builtin\$\$seed = moonbitlang\$core\$builtin\$\$random_seed\(\);)$/m, 16 | '// $1\nvar moonbitlang$$core$$builtin$$$$seed = 123456789;' 17 | ); 18 | 19 | // 2. run() の即時実行を削除 20 | content = content.replace( 21 | /^\(\(\) => \{\s*mizchi\$luna\$sol\$\$run\([^)]+\);\s*\}\)\(\);$/m, 22 | '// run() removed for Cloudflare Workers' 23 | ); 24 | 25 | writeFileSync(file, content); 26 | console.log(`Patched: ${file}`); 27 | -------------------------------------------------------------------------------- /src/tests/browser_components/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/tests/browser_components" 3 | 4 | import( 5 | "mizchi/js/browser/dom" 6 | "mizchi/js/core" 7 | ) 8 | 9 | // Values 10 | pub fn hydrate_dynamic_attrs(@dom.Element, @core.Any) -> Unit 11 | 12 | pub fn hydrate_element_ref(@dom.Element, @core.Any) -> Unit 13 | 14 | pub fn hydrate_events(@dom.Element, @core.Any) -> Unit 15 | 16 | pub fn hydrate_for_each(@dom.Element, @core.Any) -> Unit 17 | 18 | pub fn hydrate_input_binding(@dom.Element, @core.Any) -> Unit 19 | 20 | pub fn hydrate_show_toggle(@dom.Element, @core.Any) -> Unit 21 | 22 | pub fn hydrate_signal_effect(@dom.Element, @core.Any) -> Unit 23 | 24 | pub fn hydrate_sortable_list(@dom.Element, @core.Any) -> Unit 25 | 26 | // Errors 27 | 28 | // Types and methods 29 | 30 | // Type aliases 31 | 32 | // Traits 33 | 34 | -------------------------------------------------------------------------------- /examples/astra_app/docs/posts/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Posts 3 | description: Blog posts example with dynamic routes 4 | --- 5 | 6 | # Posts 7 | 8 | This example demonstrates the `_slug_` pattern for static site generation with dynamic routes. 9 | 10 | ## Available Posts 11 | 12 | - [Hello World](/posts/hello-world/) 13 | - [Getting Started](/posts/getting-started/) 14 | - [Advanced Topics](/posts/advanced-topics/) 15 | 16 | ## How Dynamic Routes Work 17 | 18 | In Astra SSG, you can create dynamic routes using the `_param_` directory naming convention: 19 | 20 | ``` 21 | posts/ 22 | index.md # Posts listing (this page) 23 | _slug_/ # Dynamic parameter directory 24 | page.json # Static params configuration 25 | index.md # Template for each page 26 | ``` 27 | 28 | At build time, Astra reads `page.json` and generates a page for each entry in `staticParams`. 29 | -------------------------------------------------------------------------------- /src/internal/json_utils/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/internal/json_utils" 3 | 4 | // Values 5 | pub fn extract_array(Map[String, Json], String) -> Array[Json] 6 | 7 | pub fn extract_bool(Map[String, Json], String, Bool) -> Bool 8 | 9 | pub fn extract_int(Map[String, Json], String, Int) -> Int 10 | 11 | pub fn extract_int_opt(Map[String, Json], String) -> Int? 12 | 13 | pub fn extract_object(Map[String, Json], String) -> Map[String, Json]? 14 | 15 | pub fn extract_string(Map[String, Json], String, String) -> String 16 | 17 | pub fn extract_string_array(Map[String, Json], String) -> Array[String] 18 | 19 | pub fn extract_string_opt(Map[String, Json], String) -> String? 20 | 21 | pub fn parse_string_array(Array[Json]) -> Array[String] 22 | 23 | // Errors 24 | 25 | // Types and methods 26 | 27 | // Type aliases 28 | 29 | // Traits 30 | 31 | -------------------------------------------------------------------------------- /src/sol/router/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { "path": "mizchi/luna/luna", "alias": "luna" }, 7 | { "path": "mizchi/luna/luna/routes", "alias": "routes" }, 8 | { "path": "mizchi/luna/luna/render", "alias": "render" }, 9 | { "path": "mizchi/luna/core/ssg", "alias": "ssg" }, 10 | { "path": "mizchi/luna/core/routes", "alias": "core_routes" }, 11 | { "path": "mizchi/luna/platform/server_dom", "alias": "server_dom" }, 12 | { "path": "mizchi/luna/sol/middleware", "alias": "middleware" }, 13 | { "path": "mizchi/luna/sol/isr", "alias": "isr" }, 14 | { "path": "mizchi/npm_typed/hono", "alias": "hono" }, 15 | { "path": "mizchi/js/core", "alias": "js" }, 16 | { "path": "mizchi/js/web/http", "alias": "http" }, 17 | { "path": "mizchi/js/web/url", "alias": "url" } 18 | ], 19 | "warn-list": "-24-67-29" 20 | } 21 | -------------------------------------------------------------------------------- /src/platform/server_dom/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" }, 5 | { "path": "mizchi/luna/luna/render", "alias": "render" }, 6 | { "path": "mizchi/luna/platform/server_dom/element", "alias": "element" } 7 | ], 8 | "test-import": [ 9 | { "path": "mizchi/luna/platform/server_dom/element", "alias": "element" } 10 | ], 11 | "link": { 12 | "js": { 13 | "exports": [ 14 | "render", 15 | "render_with_preloads", 16 | "render_document", 17 | "render_document_with_preloads", 18 | "generate_preload_tags", 19 | "document", 20 | "client", 21 | "wc_island", 22 | "wc_island_load", 23 | "wc_island_idle", 24 | "wc_island_visible", 25 | "wc_island_media" 26 | ], 27 | "format": "esm" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/astra/generator/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": ["js"], 3 | "import": [ 4 | { "path": "mizchi/luna/luna", "alias": "luna" }, 5 | { "path": "mizchi/luna/luna/render", "alias": "render" }, 6 | { "path": "mizchi/luna/astra", "alias": "astra" }, 7 | { "path": "mizchi/luna/astra/markdown", "alias": "markdown" }, 8 | { "path": "mizchi/luna/astra/routes", "alias": "routes" }, 9 | { "path": "mizchi/luna/astra/shiki", "alias": "shiki" }, 10 | { "path": "mizchi/luna/astra/assets", "alias": "assets" }, 11 | { "path": "mizchi/luna/astra/tree", "alias": "tree" }, 12 | { "path": "mizchi/luna/core/ssg", "alias": "ssg" }, 13 | { "path": "mizchi/luna/core/routes", "alias": "core_routes" }, 14 | { "path": "mizchi/js/core", "alias": "js" }, 15 | { "path": "mizchi/js/node/fs", "alias": "fs" }, 16 | { "path": "mizchi/js/node/path", "alias": "path" } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /vitest.browser.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | import { playwright } from "@vitest/browser-playwright"; 3 | 4 | export default defineConfig({ 5 | esbuild: { 6 | jsx: "automatic", 7 | jsxImportSource: "@luna_ui/luna", 8 | }, 9 | test: { 10 | reporters: ["dot"], 11 | browser: { 12 | enabled: true, 13 | headless: true, 14 | provider: playwright(), 15 | instances: [ 16 | { browser: "chromium" }, 17 | ], 18 | }, 19 | include: [ 20 | "js/luna/**/*.test.ts", 21 | ], 22 | exclude: [ 23 | "**/node_modules/**", 24 | "**/.mooncakes/**", 25 | "js/**/tmp/**", 26 | // TSX tests use global-jsdom (jsdom environment only, not real browser) 27 | "js/luna/**/*.test.tsx", 28 | // Benchmarks should only run with `vitest bench` 29 | "js/luna/**/*.bench.ts", 30 | ], 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /examples/astra_app/docs/about/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | description: About this demo site 4 | --- 5 | 6 | # About 7 | 8 | This demo showcases the Luna framework's **Unified Progressive Architecture**. 9 | 10 | ## What is Luna? 11 | 12 | Luna is a UI library for MoonBit that provides: 13 | 14 | 1. **Island Architecture** - Only hydrate interactive parts 15 | 2. **Signal-based Reactivity** - Fine-grained updates 16 | 3. **SSR + Hydration** - Server render with client interactivity 17 | 18 | ## Implementation Phases 19 | 20 | | Phase | Description | Status | 21 | |-------|-------------|--------| 22 | | 1 | Type definitions | Complete | 23 | | 2 | Directory scanner | Complete | 24 | | 3 | Client runtime | Complete | 25 | | 4 | Build pipeline | Complete | 26 | | 5 | SSR components | Complete | 27 | | 6 | CFW deploy | Complete | 28 | | 7 | Extended routers | Complete | 29 | | 8 | Lint & DX | Complete | 30 | 31 | ## Links 32 | 33 | - [Home](/) 34 | - [Guide](/guide/) 35 | -------------------------------------------------------------------------------- /src/astra/assets/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/astra/assets" 3 | 4 | import( 5 | "mizchi/luna/astra" 6 | ) 7 | 8 | // Values 9 | pub fn clear_cache() -> Unit 10 | 11 | pub fn get_default_css(@astra.SsgConfig) -> String 12 | 13 | pub fn get_github_markdown_css() -> String 14 | 15 | pub fn get_island_loader_js() -> String 16 | 17 | pub fn get_loader_js() -> String 18 | 19 | pub fn get_main_css() -> String 20 | 21 | pub fn get_markdown_css() -> String 22 | 23 | pub fn get_sidebar_js() -> String 24 | 25 | pub fn get_sidebar_script() -> String 26 | 27 | pub fn get_theme_js() -> String 28 | 29 | pub fn get_theme_script() -> String 30 | 31 | pub fn get_toc_js() -> String 32 | 33 | pub fn get_toc_script() -> String 34 | 35 | pub fn get_wc_loader_js() -> String 36 | 37 | pub fn get_wc_loader_script() -> String 38 | 39 | // Errors 40 | 41 | // Types and methods 42 | 43 | // Type aliases 44 | 45 | // Traits 46 | 47 | -------------------------------------------------------------------------------- /js/astra/assets/scripts/theme.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var theme = localStorage.getItem('theme'); 3 | var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; 4 | var isDark = theme === 'dark' || (theme !== 'light' && prefersDark); 5 | if (isDark) { 6 | document.documentElement.classList.add('dark'); 7 | document.documentElement.classList.remove('light'); 8 | } else { 9 | document.documentElement.classList.add('light'); 10 | document.documentElement.classList.remove('dark'); 11 | } 12 | window.toggleTheme = function() { 13 | var html = document.documentElement; 14 | var wasDark = html.classList.contains('dark'); 15 | if (wasDark) { 16 | html.classList.remove('dark'); 17 | html.classList.add('light'); 18 | localStorage.setItem('theme', 'light'); 19 | } else { 20 | html.classList.add('dark'); 21 | html.classList.remove('light'); 22 | localStorage.setItem('theme', 'dark'); 23 | } 24 | }; 25 | })(); 26 | -------------------------------------------------------------------------------- /src/astra/assets/scripts/theme.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var theme = localStorage.getItem('theme'); 3 | var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; 4 | var isDark = theme === 'dark' || (theme !== 'light' && prefersDark); 5 | if (isDark) { 6 | document.documentElement.classList.add('dark'); 7 | document.documentElement.classList.remove('light'); 8 | } else { 9 | document.documentElement.classList.add('light'); 10 | document.documentElement.classList.remove('dark'); 11 | } 12 | window.toggleTheme = function() { 13 | var html = document.documentElement; 14 | var wasDark = html.classList.contains('dark'); 15 | if (wasDark) { 16 | html.classList.remove('dark'); 17 | html.classList.add('light'); 18 | localStorage.setItem('theme', 'light'); 19 | } else { 20 | html.classList.add('dark'); 21 | html.classList.remove('light'); 22 | localStorage.setItem('theme', 'dark'); 23 | } 24 | }; 25 | })(); 26 | -------------------------------------------------------------------------------- /src/luna/_check.mbt: -------------------------------------------------------------------------------- 1 | // ///| 2 | // struct TestStruct { 3 | // value : String 4 | // internal_func : () -> String 5 | // } 6 | 7 | // ///| 8 | // pub fn TestStruct::fff(self : Self) -> String { 9 | // self.value 10 | // } 11 | 12 | // ///| 13 | // struct TestStructWrapper(TestStruct) 14 | 15 | // ///| 16 | // fn TestStruct::outer(self : Self) -> TestStructWrapper { 17 | // TestStructWrapper(self) 18 | // } 19 | 20 | // externa 21 | 22 | // pub fn[X] inspect_x(s : X) -> { 23 | // inspect((s.0).fff()) 24 | // } 25 | 26 | // ///| 27 | // test { 28 | // let inner = TestStruct::{ 29 | // value: "Hello".to_string(), 30 | // internal_func: fn() { "Internal".to_string() }, 31 | // } 32 | // let outer = TestStructWrapper(inner) 33 | // // outer.value 34 | // (outer.internal_func)() |> inspect 35 | 36 | // // inspect(outer.0.fff()) 37 | // // inspect(outer.0.fff()) 38 | 39 | // // let wrapped = s.outer() 40 | // // let unwrapped = wrapped.0 41 | // // assert(unwrapped.fff() == "Hello") 42 | // } 43 | -------------------------------------------------------------------------------- /js/astra/assets/scripts/sidebar.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var STORAGE_KEY = 'astra-sidebar-state'; 3 | function loadState() { 4 | try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {}; } catch { return {}; } 5 | } 6 | function saveState(state) { 7 | try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch {} 8 | } 9 | function initSidebar() { 10 | var state = loadState(); 11 | document.querySelectorAll('[data-sidebar-group]').forEach(function(el) { 12 | var key = el.dataset.sidebarGroup; 13 | if (key && state[key] !== undefined) { 14 | el.open = state[key]; 15 | } 16 | el.addEventListener('toggle', function() { 17 | var s = loadState(); 18 | s[key] = el.open; 19 | saveState(s); 20 | }); 21 | }); 22 | } 23 | if (document.readyState === 'loading') { 24 | document.addEventListener('DOMContentLoaded', initSidebar); 25 | } else { 26 | initSidebar(); 27 | } 28 | window.__ASTRA_INIT_SIDEBAR__ = initSidebar; 29 | })(); 30 | -------------------------------------------------------------------------------- /src/astra/assets/scripts/sidebar.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var STORAGE_KEY = 'astra-sidebar-state'; 3 | function loadState() { 4 | try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {}; } catch { return {}; } 5 | } 6 | function saveState(state) { 7 | try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch {} 8 | } 9 | function initSidebar() { 10 | var state = loadState(); 11 | document.querySelectorAll('[data-sidebar-group]').forEach(function(el) { 12 | var key = el.dataset.sidebarGroup; 13 | if (key && state[key] !== undefined) { 14 | el.open = state[key]; 15 | } 16 | el.addEventListener('toggle', function() { 17 | var s = loadState(); 18 | s[key] = el.open; 19 | saveState(s); 20 | }); 21 | }); 22 | } 23 | if (document.readyState === 'loading') { 24 | document.addEventListener('DOMContentLoaded', initSidebar); 25 | } else { 26 | initSidebar(); 27 | } 28 | window.__ASTRA_INIT_SIDEBAR__ = initSidebar; 29 | })(); 30 | -------------------------------------------------------------------------------- /e2e/sol-app/playwright.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test"; 2 | import { dirname, join } from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | /** 8 | * Playwright config for sol-app E2E tests 9 | */ 10 | export default defineConfig({ 11 | testDir: __dirname, 12 | testMatch: "**/*.test.ts", 13 | fullyParallel: false, 14 | forbidOnly: !!process.env.CI, 15 | retries: process.env.CI ? 2 : 0, 16 | workers: 1, 17 | reporter: "list", 18 | use: { 19 | baseURL: "http://localhost:3457", 20 | trace: "on-first-retry", 21 | }, 22 | projects: [ 23 | { 24 | name: "chromium", 25 | use: { ...devices["Desktop Chrome"] }, 26 | }, 27 | ], 28 | webServer: { 29 | command: `bash ${join(__dirname, "setup.sh")}`, 30 | url: "http://127.0.0.1:3457", 31 | reuseExistingServer: !process.env.CI, 32 | stdout: "pipe", 33 | timeout: 120000, // 2 minutes for build + start 34 | }, 35 | }); 36 | -------------------------------------------------------------------------------- /src/tests/ssr_test_component/ssr_test_component.mbt: -------------------------------------------------------------------------------- 1 | // SSR Test Component - demonstrates SSR rendering 2 | 3 | ///| 4 | /// Create a server-side element 5 | fn h( 6 | tag : String, 7 | attrs : Array[(String, @luna.Attr[Unit])], 8 | children : Array[@luna.Node[Unit]], 9 | ) -> @luna.Node[Unit] { 10 | @luna.h(tag, attrs, children) 11 | } 12 | 13 | ///| 14 | /// Create a static attribute 15 | fn attr(key : String, value : String) -> (String, @luna.Attr[Unit]) { 16 | (key, @luna.attr_static(value)) 17 | } 18 | 19 | ///| 20 | /// Render function - exported for SSR 21 | pub fn render() -> @luna.Node[Unit] { 22 | h("div", [attr("class", "ssr-test-component")], [ 23 | h("h2", [], [@luna.text("SSR Test Component")]), 24 | h("p", [], [@luna.text("This component was rendered on the server!")]), 25 | h("ul", [], [ 26 | h("li", [], [@luna.text("Server-side rendered content")]), 27 | h("li", [], [@luna.text("No JavaScript required")]), 28 | h("li", [], [@luna.text("Fast initial load")]), 29 | ]), 30 | ]) 31 | } 32 | -------------------------------------------------------------------------------- /src/platform/dom/portal/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/platform/dom/portal" 3 | 4 | import( 5 | "mizchi/js/browser/dom" 6 | ) 7 | 8 | // Values 9 | pub fn portal(PortalOptions, Array[DomNode]) -> DomNode 10 | 11 | pub fn portal_to_body(Array[DomNode]) -> DomNode 12 | 13 | pub fn portal_to_selector(String, Array[DomNode]) -> DomNode 14 | 15 | pub fn portal_to_with_shadow(@dom.Element, Array[DomNode]) -> DomNode 16 | 17 | pub fn portal_with_shadow(Array[DomNode]) -> DomNode 18 | 19 | // Errors 20 | 21 | // Types and methods 22 | pub(all) enum DomNode { 23 | El(@dom.Element) 24 | Txt(@dom.Text) 25 | Raw(@dom.Node) 26 | } 27 | pub fn DomNode::to_jsdom(Self) -> @dom.Node 28 | 29 | pub struct PortalOptions { 30 | mount : @dom.Element? 31 | use_shadow : Bool 32 | is_svg : Bool 33 | } 34 | pub fn PortalOptions::default() -> Self 35 | pub fn PortalOptions::with_mount(@dom.Element) -> Self 36 | pub fn PortalOptions::with_shadow() -> Self 37 | 38 | // Type aliases 39 | 40 | // Traits 41 | 42 | -------------------------------------------------------------------------------- /src/platform/server_dom/element/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": [ 3 | { "path": "mizchi/luna/luna", "alias": "luna" } 4 | ], 5 | "link": { 6 | "js": { 7 | "exports": [ 8 | "html", 9 | "head", 10 | "body", 11 | "title", 12 | "meta", 13 | "link", 14 | "script", 15 | "style_", 16 | "main_", 17 | "nav", 18 | "header_", 19 | "footer_", 20 | "section", 21 | "article", 22 | "aside", 23 | "div", 24 | "span", 25 | "p", 26 | "a", 27 | "h1", 28 | "h2", 29 | "h3", 30 | "h4", 31 | "h5", 32 | "h6", 33 | "ul", 34 | "ol", 35 | "li", 36 | "img", 37 | "br", 38 | "hr", 39 | "button", 40 | "text", 41 | "fragment", 42 | "attr", 43 | "sol_link", 44 | "outlet", 45 | "template_outlet", 46 | "template_title" 47 | ], 48 | "format": "esm" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/platform/js/stream_renderer/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/platform/js/stream_renderer" 3 | 4 | import( 5 | "mizchi/js/web/streams" 6 | "mizchi/luna/luna" 7 | ) 8 | 9 | // Values 10 | pub fn render_to_readable_stream(@luna.Node[Unit]) -> @streams.ReadableStream 11 | 12 | pub fn render_to_stream(@luna.Node[Unit], StreamWriter) -> Unit 13 | 14 | pub fn render_to_stream_collecting_async(@luna.Node[Unit], StreamWriter) -> Array[AsyncBoundary] 15 | 16 | pub async fn render_to_stream_with_async(@luna.Node[Unit], StreamWriter) -> Unit 17 | 18 | pub fn stream_async_replacement(Int, StreamWriter, (StreamWriter) -> Unit) -> Unit 19 | 20 | // Errors 21 | 22 | // Types and methods 23 | pub(all) struct AsyncBoundary { 24 | id : Int 25 | async_node : @luna.VAsync[Unit] 26 | } 27 | 28 | pub struct StreamWriter((String) -> Unit) 29 | pub fn StreamWriter::from_callback((String) -> Unit) -> Self 30 | #deprecated 31 | pub fn StreamWriter::inner(Self) -> (String) -> Unit 32 | 33 | // Type aliases 34 | 35 | // Traits 36 | 37 | -------------------------------------------------------------------------------- /examples/astra_app/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Astra Demo 3 | description: Demo site for Unified Progressive Architecture 4 | --- 5 | 6 | # Astra Demo 7 | 8 | This is a demo site showcasing the **Unified Progressive Architecture** features. 9 | 10 | ## Features 11 | 12 | - **Static Site Generation** - Markdown pages compiled to HTML 13 | - **Component Integration** - MoonBit components with SSR + Hydration 14 | - **Client Router** - Hybrid navigation with scroll restoration 15 | - **Cloudflare Deploy** - Optimized for Cloudflare Pages 16 | 17 | ## Pages 18 | 19 | - [About](/about/) - About this demo 20 | - [Guide](/guide/) - Getting started guide 21 | 22 | ## Architecture 23 | 24 | ``` 25 | docs/ 26 | ├── index.md # Home page (static) 27 | ├── about/ 28 | │ └── index.md # About page (static) 29 | └── guide/ 30 | └── index.md # Guide page (static) 31 | ``` 32 | 33 | ## Build Output 34 | 35 | When built with `deploy.target: "cloudflare"`: 36 | 37 | - `dist/_routes.json` - Cloudflare Pages routing 38 | - `dist/_luna/manifest.json` - Client chunk manifest 39 | -------------------------------------------------------------------------------- /src/platform/js/cache/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/platform/js/cache" 3 | 4 | import( 5 | "mizchi/luna/core/env" 6 | ) 7 | 8 | // Values 9 | 10 | // Errors 11 | 12 | // Types and methods 13 | pub(all) struct CacheEntry { 14 | mtime_ms : Double 15 | cached_at : Double 16 | } 17 | pub impl Eq for CacheEntry 18 | pub impl Show for CacheEntry 19 | 20 | pub struct FileCache { 21 | entries : Map[String, CacheEntry] 22 | contents : Map[String, String] 23 | } 24 | pub fn FileCache::clear(Self) -> Unit 25 | pub fn FileCache::invalidate(Self, String) -> Unit 26 | pub fn[FS : @env.FileSystem] FileCache::is_stale(Self, FS, String) -> Bool 27 | pub fn FileCache::len(Self) -> Int 28 | pub fn FileCache::new() -> Self 29 | pub fn[FS : @env.FileSystem] FileCache::persist(Self, FS, String) -> Unit raise 30 | pub fn[FS : @env.FileSystem] FileCache::read_file(Self, FS, String) -> String raise 31 | pub fn[FS : @env.FileSystem] FileCache::restore(Self, FS, String) -> Unit raise 32 | 33 | // Type aliases 34 | 35 | // Traits 36 | 37 | -------------------------------------------------------------------------------- /src/platform/server_dom/element/sol_link.mbt: -------------------------------------------------------------------------------- 1 | // Sol Link - CSR navigation link component 2 | // 3 | // Creates an anchor element with data-sol-link attribute 4 | // for client-side navigation without full page reload. 5 | // Create a Sol Link for CSR navigation 6 | // 7 | // Renders as `` which is intercepted 8 | // 9 | 10 | ///| 11 | /// by sol-nav.js for client-side navigation. 12 | pub fn sol_link( 13 | href~ : String, 14 | children : Array[@luna.Node[Unit]], 15 | id? : String, 16 | class? : String, 17 | style? : String, 18 | prefetch? : Bool, 19 | replace? : Bool, 20 | attrs? : Array[(String, @luna.Attr[Unit])], 21 | ) -> @luna.Node[Unit] { 22 | let props = build_attrs(id, class, style, attrs) 23 | props.push(("href", static_attr(href))) 24 | props.push(("data-sol-link", static_attr(""))) 25 | if prefetch is Some(true) { 26 | props.push(("data-sol-prefetch", static_attr(""))) 27 | } 28 | if replace is Some(true) { 29 | props.push(("data-sol-replace", static_attr(""))) 30 | } 31 | create_element("a", props, children) 32 | } 33 | -------------------------------------------------------------------------------- /examples/sol_app/app/server/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "supported-targets": [ 3 | "js" 4 | ], 5 | "import": [ 6 | { 7 | "path": "mizchi/luna/luna", 8 | "alias": "luna" 9 | }, 10 | { 11 | "path": "mizchi/luna/sol", 12 | "alias": "sol" 13 | }, 14 | { 15 | "path": "mizchi/luna/sol/router", 16 | "alias": "router" 17 | }, 18 | { 19 | "path": "mizchi/luna/sol/middleware", 20 | "alias": "mw" 21 | }, 22 | { 23 | "path": "mizchi/luna/sol/action", 24 | "alias": "action" 25 | }, 26 | { 27 | "path": "mizchi/luna/platform/server_dom", 28 | "alias": "server_dom" 29 | }, 30 | { 31 | "path": "mizchi/luna/platform/server_dom/element", 32 | "alias": "element" 33 | }, 34 | { 35 | "path": "mizchi/js/core", 36 | "alias": "core" 37 | }, 38 | { 39 | "path": "example/sol-app/__gen__/types", 40 | "alias": "types" 41 | }, 42 | { 43 | "path": "example/sol-app/client", 44 | "alias": "client" 45 | } 46 | ], 47 | "warn-list": "-35" 48 | } -------------------------------------------------------------------------------- /docs/ja/01_luna/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Luna 3 | --- 4 | 5 | # Luna 6 | 7 | Luna は MoonBit と JavaScript のためのリアクティブ UI ライブラリです。細粒度リアクティビティと Island Architecture を特徴としています。 8 | 9 | ## コア概念 10 | 11 | ### [なぜ Luna?](/ja/luna/why-luna/) 12 | 13 | Luna の設計思想、パフォーマンス特性、他のフレームワークとの比較について学びます。 14 | 15 | - 最小限のランタイム(ローダー ~1.6KB) 16 | - Virtual DOM を使わない細粒度リアクティビティ 17 | - 最適なパフォーマンスのための Island Architecture 18 | 19 | ### [Signals](/ja/luna/signals/) 20 | 21 | Signals は Luna のリアクティビティシステムの基盤です。リアクティブプリミティブの作成と使用方法を学びます。 22 | 23 | - `createSignal` - リアクティブな値を作成 24 | - `createEffect` - Signal の変更に反応 25 | - `createMemo` - 派生値を計算 26 | 27 | ### [Islands](/ja/luna/islands/) 28 | 29 | Island Architecture は部分的なハイドレーションを可能にし、必要な場所にのみ JavaScript を配信します。 30 | 31 | - 静的コンテンツは静的なまま 32 | - インタラクティブなコンポーネントは独立してハイドレーション 33 | - 複数のトリガー戦略(load, idle, visible, media) 34 | 35 | ## クイックリンク 36 | 37 | - [チュートリアル (MoonBit)](/ja/tutorial-moonbit/) - MoonBit で Luna を学ぶ 38 | - [チュートリアル (JavaScript)](/ja/tutorial-js/) - JavaScript で Luna を学ぶ 39 | - [Astra SSG](/ja/astra/) - 静的サイトジェネレーター 40 | - [Sol Framework](/ja/sol/) - フルスタック SSR フレームワーク 41 | -------------------------------------------------------------------------------- /examples/astra_app/docs/ssr_test_component/ssr_test_component.mbt: -------------------------------------------------------------------------------- 1 | // SSR Test Component - locally defined component for SSR demo 2 | // TODO: @element DSL は DomNode を返すため SSR では使えない 3 | // SSR用のDSL (@luna.div, @luna.p 等) を検討する 4 | 5 | ///| 6 | fn h( 7 | tag : String, 8 | attrs : Array[(String, @luna.Attr[Unit])], 9 | children : Array[@luna.Node[Unit]], 10 | ) -> @luna.Node[Unit] { 11 | @luna.h(tag, attrs, children) 12 | } 13 | 14 | ///| 15 | fn attr(key : String, value : String) -> (String, @luna.Attr[Unit]) { 16 | (key, @luna.attr_static(value)) 17 | } 18 | 19 | ///| 20 | /// Render function - exported for SSR 21 | pub fn render() -> @luna.Node[Unit] { 22 | h("div", [attr("class", "ssr-local-component")], [ 23 | h("h2", [], [@luna.text("Local SSR Component")]), 24 | h("p", [], [ 25 | @luna.text("This component is defined in docs/ssr_test_component/"), 26 | ]), 27 | h("ul", [], [ 28 | h("li", [], [@luna.text("Built from local .mbt file")]), 29 | h("li", [], [@luna.text("Part of astra_app example")]), 30 | h("li", [], [@luna.text("SSR rendered on server")]), 31 | ]), 32 | ]) 33 | } 34 | -------------------------------------------------------------------------------- /src/sol/isr/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/sol/isr" 3 | 4 | // Values 5 | pub fn cache_key(String, Array[(String, String)]) -> String 6 | 7 | pub fn check_status(CacheEntry?, Int64) -> CacheStatus 8 | 9 | pub fn now_ms() -> Int64 10 | 11 | // Errors 12 | 13 | // Types and methods 14 | pub struct CacheEntry { 15 | html : String 16 | generated_at : Int64 17 | revalidate : Int 18 | } 19 | pub fn CacheEntry::new(String, Int64, Int) -> Self 20 | 21 | pub enum CacheStatus { 22 | Fresh 23 | Stale 24 | Miss 25 | } 26 | 27 | pub struct ISRConfig { 28 | enabled : Bool 29 | default_revalidate : Int 30 | } 31 | pub fn ISRConfig::default() -> Self 32 | 33 | pub struct MemoryCache { 34 | store : Map[String, CacheEntry] 35 | } 36 | pub fn MemoryCache::new() -> Self 37 | pub impl ISRCache for MemoryCache 38 | 39 | // Type aliases 40 | 41 | // Traits 42 | pub(open) trait ISRCache { 43 | get(Self, String) -> CacheEntry? 44 | put(Self, String, CacheEntry) -> Unit 45 | delete(Self, String) -> Unit 46 | schedule_revalidation(Self, async () -> Unit) -> Unit 47 | } 48 | 49 | -------------------------------------------------------------------------------- /js/luna/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@luna_ui/luna", 3 | "version": "0.1.5", 4 | "description": "Fine-grained reactive UI library for Moonbit/JS", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "exports": { 9 | ".": { 10 | "types": "./dist/index.d.ts", 11 | "import": "./dist/index.js" 12 | }, 13 | "./jsx-runtime": { 14 | "types": "./dist/jsx-runtime.d.ts", 15 | "import": "./dist/jsx-runtime.js" 16 | }, 17 | "./jsx-dev-runtime": { 18 | "types": "./dist/jsx-dev-runtime.d.ts", 19 | "import": "./dist/jsx-dev-runtime.js" 20 | } 21 | }, 22 | "scripts": { 23 | "prepublishOnly": "pnpm build", 24 | "build": "tsdown", 25 | "test": "vitest run", 26 | "build:examples": "vite build examples" 27 | }, 28 | "devDependencies": { 29 | "global-jsdom": "^27.0.0", 30 | "jsdom": "^27.3.0", 31 | "preact": "^10.28.0", 32 | "tsdown": "^0.18.2" 33 | }, 34 | "repository": { 35 | "directory": "js/luna", 36 | "url": "https://github.com/mizchi/luna.mbt" 37 | }, 38 | "files": [ 39 | "dist" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /js/astra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@luna_ui/astra", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "description": "MoonBit Island Architecture SSG Framework", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "exports": { 9 | ".": { 10 | "types": "./dist/index.d.ts", 11 | "import": "./dist/index.js" 12 | } 13 | }, 14 | "bin": { 15 | "astra": "./bin/astra.mjs" 16 | }, 17 | "scripts": { 18 | "prepublishOnly": "pnpm build", 19 | "build": "tsdown" 20 | }, 21 | "files": [ 22 | "bin", 23 | "dist", 24 | "assets" 25 | ], 26 | "keywords": [ 27 | "luna", 28 | "moonbit", 29 | "island", 30 | "ssg", 31 | "astra" 32 | ], 33 | "author": "mizchi", 34 | "license": "MIT", 35 | "repository": { 36 | "directory": "js/astra", 37 | "url": "https://github.com/mizchi/luna.mbt" 38 | }, 39 | "dependencies": { 40 | "@hono/node-server": "^1.14.0", 41 | "colorette": "^2.0.20", 42 | "gray-matter": "^4.0.3", 43 | "hono": "^4.7.0", 44 | "marked": "^15.0.0" 45 | }, 46 | "devDependencies": { 47 | "tsdown": "^0.18.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/astra/tree/sitemap.mbt: -------------------------------------------------------------------------------- 1 | ///| 2 | /// Sitemap Generator 3 | /// DocumentTree から sitemap.xml を生成する 4 | /// sitemap.xml を生成 5 | pub fn generate_sitemap(tree : @ssg.DocumentTree) -> String { 6 | let mut urls = "" 7 | for page in tree.pages { 8 | urls = urls + generate_sitemap_url(page, tree.site.base_url) 9 | } 10 | "\n\n" + 11 | urls + 12 | "\n" 13 | } 14 | 15 | ///| 16 | fn generate_sitemap_url(page : @ssg.PageInfo, base_url : String) -> String { 17 | let url = page.canonical_url(base_url) 18 | let lastmod = iso_to_date(page.updated_at) 19 | " \n " + 20 | escape_xml(url) + 21 | "\n" + 22 | (if lastmod != "" { " " + lastmod + "\n" } else { "" }) + 23 | " \n" 24 | } 25 | 26 | ///| 27 | /// ISO 8601 日時を日付のみに変換 28 | /// "2024-12-22T12:00:00.000Z" -> "2024-12-22" 29 | fn iso_to_date(iso_date : String) -> String { 30 | if iso_date.length() >= 10 { 31 | iso_date[:10].to_string() catch { 32 | _ => iso_date 33 | } 34 | } else { 35 | iso_date 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/astra/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 🌗 Luna SSG 3 | description: A static site generator built with MoonBit 4 | layout: home 5 | --- 6 | 7 | # Luna SSG 8 | 9 | A static site generator built with MoonBit. Generate VitePress/Docusaurus-style documentation sites. 10 | 11 | ## Features 12 | 13 | - **File-based Routing** - Auto-generate routes from Markdown files in `docs/` 14 | - **Frontmatter Support** - Define page metadata in YAML format 15 | - **VitePress-style Theme** - Auto-generated navigation, sidebar, and table of contents 16 | - **Multi-language Support** - i18n with automatic fallback to default locale 17 | - **Island Architecture** - Partial hydration support (coming soon) 18 | 19 | ## Quick Start 20 | 21 | ```bash 22 | # Create docs directory 23 | mkdir docs 24 | 25 | # Add a Markdown file 26 | echo "# Hello World" > docs/index.md 27 | 28 | # Build the site 29 | sol ssg build 30 | ``` 31 | 32 | ## Configuration 33 | 34 | ```json 35 | { 36 | "ssg": { 37 | "docs": "docs", 38 | "output": "dist", 39 | "title": "My Documentation", 40 | "nav": [ 41 | { "text": "Guide", "link": "/guide/" } 42 | ] 43 | } 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /src/platform/dom/client/repair/experimental_hydrate_wbtest.mbt: -------------------------------------------------------------------------------- 1 | // Whitebox tests for Experimental Hydration - internal coverage 2 | // 3 | 4 | ///| 5 | test "ExperimentalHydrationResult Failed variant" { 6 | // Test that Failed variant can be constructed and matched 7 | let result : ExperimentalHydrationResult = Failed("Test error message") 8 | match result { 9 | Failed(msg) => assert_eq(msg, "Test error message") 10 | _ => assert_true(false) 11 | } 12 | } 13 | 14 | ///| 15 | test "ExperimentalHydrationResult all variants" { 16 | // Clean variant 17 | let clean : ExperimentalHydrationResult = Clean 18 | match clean { 19 | Clean => assert_true(true) 20 | _ => assert_true(false) 21 | } 22 | 23 | // Repaired variant 24 | let repaired : ExperimentalHydrationResult = Repaired(["repair1", "repair2"]) 25 | match repaired { 26 | Repaired(repairs) => assert_eq(repairs.length(), 2) 27 | _ => assert_true(false) 28 | } 29 | 30 | // Failed variant 31 | let failed : ExperimentalHydrationResult = Failed("error") 32 | match failed { 33 | Failed(err) => assert_eq(err, "error") 34 | _ => assert_true(false) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/astra/generator/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/astra/generator" 3 | 4 | import( 5 | "mizchi/luna/astra" 6 | "mizchi/luna/astra/shiki" 7 | "mizchi/luna/core/ssg" 8 | ) 9 | 10 | // Values 11 | pub fn copy_static_assets(@astra.BuildContext) -> Unit 12 | 13 | pub fn generate_404_page(@astra.BuildContext) -> Unit 14 | 15 | pub fn generate_client_manifest(@astra.BuildContext) -> Unit 16 | 17 | pub fn generate_meta_files(@astra.BuildContext, @ssg.DocumentTree) -> Unit 18 | 19 | pub fn generate_routes_json(Array[@astra.PageMeta]) -> String 20 | 21 | pub fn generate_single_page(@astra.BuildContext, @astra.PageMeta, @shiki.Highlighter) -> Result[Unit, String] 22 | 23 | pub fn generate_site(@astra.SsgConfig, String) -> Result[Unit, String] 24 | 25 | pub async fn generate_site_async(@astra.SsgConfig, String) -> Result[Unit, String] 26 | 27 | pub fn generate_site_with_highlighter(@astra.SsgConfig, String, @shiki.Highlighter) -> Result[Unit, String] 28 | 29 | pub fn write_routes_json(@astra.BuildContext) -> Unit 30 | 31 | // Errors 32 | 33 | // Types and methods 34 | 35 | // Type aliases 36 | 37 | // Traits 38 | 39 | -------------------------------------------------------------------------------- /e2e/playwright.config-sol.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test"; 2 | import { dirname, join } from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | const solAppDir = join(__dirname, "../examples/sol_app"); 7 | 8 | export default defineConfig({ 9 | testDir: __dirname, 10 | testMatch: ["**/wc_counter*.test.ts", "**/sol_*.test.ts", "**/wc-css-isolation.test.ts", "**/wc-ssr-css.test.ts"], 11 | fullyParallel: true, 12 | forbidOnly: !!process.env.CI, 13 | retries: process.env.CI ? 2 : 0, 14 | workers: process.env.CI ? 1 : undefined, 15 | reporter: "list", 16 | use: { 17 | baseURL: "http://localhost:3000", 18 | trace: "on-first-retry", 19 | }, 20 | projects: [ 21 | { 22 | name: "chromium", 23 | use: { ...devices["Desktop Chrome"] }, 24 | }, 25 | ], 26 | webServer: { 27 | command: `cd ${solAppDir} && pnpm build && node .sol/prod/server/main.js`, 28 | url: "http://127.0.0.1:3000", 29 | reuseExistingServer: !process.env.CI, 30 | stdout: "pipe", 31 | timeout: 60000, // Longer timeout for build + server startup 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /examples/astra_app/docs/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Guide to using Luna with Astra 4 | --- 5 | 6 | # Getting Started 7 | 8 | This guide shows how to use Luna's Unified Progressive Architecture. 9 | 10 | ## Installation 11 | 12 | ```bash 13 | # Install Luna CLI 14 | npm install -g @luna_ui/cli 15 | 16 | # Create new project 17 | luna init my-site 18 | cd my-site 19 | ``` 20 | 21 | ## Project Structure 22 | 23 | ``` 24 | my-site/ 25 | ├── astra.json # Configuration 26 | ├── docs/ # Content directory 27 | │ ├── index.md # Home page 28 | │ └── guide/ 29 | │ └── index.md # This page 30 | └── dist/ # Build output 31 | ``` 32 | 33 | ## Configuration 34 | 35 | Create `astra.json`: 36 | 37 | ```json 38 | { 39 | "$schema": "../../schemas/astra.schema.json", 40 | "title": "My Site", 41 | "docs_dir": "docs", 42 | "out_dir": "dist", 43 | "deploy": { 44 | "target": "cloudflare" 45 | } 46 | } 47 | ``` 48 | 49 | ## Build 50 | 51 | ```bash 52 | # Build static site 53 | astra build 54 | 55 | # Development server 56 | astra dev 57 | ``` 58 | 59 | ## Links 60 | 61 | - [Home](/) 62 | - [About](/about/) 63 | -------------------------------------------------------------------------------- /js/wcssr/src/client-entry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Web Components SSR - Client Entry 3 | * 4 | * ブラウザ専用。Hydration とイベント処理。 5 | */ 6 | 7 | // Types (クライアントで必要なもののみ) 8 | export type { 9 | State, 10 | EventPayload, 11 | Handler, 12 | RenderFn, 13 | ComponentDef, 14 | RegisterOptions, 15 | CSSStrategy, 16 | } from './types.js'; 17 | 18 | // Client functions 19 | export { 20 | registerComponent, 21 | registerComponents, 22 | defineComponent, 23 | hydrateElement, 24 | clearStyleSheetCache, 25 | getRegisteredComponents, 26 | } from './client.js'; 27 | 28 | // Parts (for MoonBit FFI integration) 29 | export { 30 | hydratePartsFromElement, 31 | NodePart, 32 | AttributePart, 33 | ChildNodePart, 34 | PropertyPart, 35 | PartGroup, 36 | } from './parts.js'; 37 | 38 | // Global API for browser (for wc-loader) 39 | import { 40 | registerComponent, 41 | registerComponents, 42 | hydrateElement, 43 | } from './client.js'; 44 | import { hydratePartsFromElement } from './parts.js'; 45 | 46 | if (typeof window !== 'undefined') { 47 | (window as any).__WCSSR__ = { 48 | registerComponent, 49 | registerComponents, 50 | hydrateElement, 51 | hydratePartsFromElement, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /src/core/env/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/core/env" 3 | 4 | // Values 5 | 6 | // Errors 7 | 8 | // Types and methods 9 | pub(all) struct StatInfo { 10 | is_file : Bool 11 | is_directory : Bool 12 | mtime_ms : Float 13 | size : Int 14 | } 15 | pub impl FileStat for StatInfo 16 | pub impl Eq for StatInfo 17 | pub impl Show for StatInfo 18 | 19 | // Type aliases 20 | 21 | // Traits 22 | pub(open) trait FileStat { 23 | isFile(Self) -> Bool 24 | isDirectory(Self) -> Bool 25 | mtime_ms(Self) -> Float 26 | size(Self) -> Int 27 | } 28 | 29 | pub(open) trait FileSystem { 30 | read_file_sync(Self, String) -> String raise 31 | write_file_sync(Self, String, String) -> Unit raise 32 | exists_sync(Self, String) -> Bool 33 | mkdir_sync(Self, String, Bool) -> Unit raise 34 | readdir_sync(Self, String) -> Array[String] raise 35 | is_file_sync(Self, String) -> Bool raise 36 | is_directory_sync(Self, String) -> Bool raise 37 | cp_sync(Self, String, String, Bool) -> Unit raise 38 | unlink_sync(Self, String) -> Unit raise 39 | rmdir_sync(Self, String, Bool) -> Unit raise 40 | stat_sync(Self, String) -> StatInfo raise 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/luna/routes/routes_match.mbt: -------------------------------------------------------------------------------- 1 | // RoutesMatch accessor methods 2 | // 3 | 4 | ///| 5 | /// コンポーネントIDを取得 6 | pub fn RoutesMatch::component(self : RoutesMatch) -> String { 7 | self.route.component 8 | } 9 | 10 | ///| 11 | /// レイアウトIDリストを取得 12 | pub fn RoutesMatch::layouts(self : RoutesMatch) -> Array[String] { 13 | self.route.layouts 14 | } 15 | 16 | ///| 17 | /// パラメータを取得 18 | pub fn RoutesMatch::get_param(self : RoutesMatch, key : String) -> String? { 19 | for pair in self.params { 20 | if pair.0 == key { 21 | return Some(pair.1) 22 | } 23 | } 24 | None 25 | } 26 | 27 | ///| 28 | /// クエリパラメータを取得 29 | pub fn RoutesMatch::get_query(self : RoutesMatch, key : String) -> String? { 30 | for pair in self.query { 31 | if pair.0 == key { 32 | return Some(pair.1) 33 | } 34 | } 35 | None 36 | } 37 | 38 | ///| 39 | /// ルートの種類を取得 40 | pub fn RoutesMatch::kind(self : RoutesMatch) -> RoutesKind { 41 | self.route.kind 42 | } 43 | 44 | ///| 45 | /// タイトルを取得 46 | pub fn RoutesMatch::title(self : RoutesMatch) -> String { 47 | self.route.title 48 | } 49 | 50 | ///| 51 | /// メタ情報を取得 52 | pub fn RoutesMatch::meta(self : RoutesMatch) -> Array[(String, String)] { 53 | self.route.meta 54 | } 55 | -------------------------------------------------------------------------------- /src/platform/server_dom/wc_island.mbt: -------------------------------------------------------------------------------- 1 | // Web Components Island helpers for server_dom 2 | // 3 | // Provides integration with Web Components based client-side components. 4 | // Uses Declarative Shadow DOM for SSR. 5 | // 6 | 7 | ///| 8 | /// Create a Web Components island for client-side hydration 9 | /// 10 | /// Example: 11 | /// ```moonbit 12 | /// wc_island( 13 | /// "wc-counter", 14 | /// "/static/wc-counter.js", 15 | /// [text("Loading...")], 16 | /// styles=":host { display: block; }", 17 | /// state="{\"count\":0}", 18 | /// ) 19 | /// ``` 20 | pub fn wc_island( 21 | name : String, 22 | url : String, 23 | children : Array[@luna.Node[Unit]], 24 | styles? : String, 25 | state? : String, 26 | trigger? : @luna.TriggerType, 27 | ) -> @luna.Node[Unit] { 28 | let styles_value = match styles { 29 | Some(s) => s 30 | None => "" 31 | } 32 | let state_value = match state { 33 | Some(s) => s 34 | None => "{}" 35 | } 36 | let trigger_value = match trigger { 37 | Some(t) => t 38 | None => @luna.Load 39 | } 40 | @luna.wc_island( 41 | name, 42 | url, 43 | styles_value, 44 | state_value, 45 | children, 46 | trigger=trigger_value, 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /experiments/css-minify/README.md: -------------------------------------------------------------------------------- 1 | # Scoped CSS Minifier 2 | 3 | MoonBit WebComponent出力のCSS圧縮実験。 4 | 5 | ## 機能 6 | 7 | 1. **CSSクラス名マングリング**: Scoped CSSなのでクラス名を短縮可能 8 | 2. **CSS空白圧縮**: `` ブロック内のCSSのみ対象 46 | - JS変数名と誤検出しないよう、CSSコンテキストからのみクラス名抽出 47 | - タグ名(button, input等)と予約語は除外 48 | 49 | ## TODO 50 | 51 | - [ ] esbuildプラグイン化 52 | - [ ] MoonBit側での統合(`scoped_html!()` マクロ等) 53 | - [ ] 複数クラス属性の完全対応 (`class="a b c"`) 54 | - [ ] CSS変数の短縮 55 | -------------------------------------------------------------------------------- /demo-src/game_react/src/index.css: -------------------------------------------------------------------------------- 1 | * { box-sizing: border-box; } 2 | body { 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace; 4 | margin: 0; 5 | padding: 20px; 6 | background: #1a1a2e; 7 | color: #eee; 8 | } 9 | .container { max-width: 800px; margin: 0 auto; } 10 | h1 { color: white; margin-bottom: 10px; } 11 | .stats { 12 | color: white; 13 | font-family: monospace; 14 | padding: 10px; 15 | background: #222; 16 | margin-bottom: 10px; 17 | } 18 | .stats span { margin-right: 20px; } 19 | .controls { 20 | padding: 10px; 21 | background: #333; 22 | margin-bottom: 10px; 23 | } 24 | .controls button { 25 | margin-right: 10px; 26 | padding: 5px 15px; 27 | } 28 | .grid-wrapper { 29 | border: 2px solid #444; 30 | display: inline-block; 31 | } 32 | .grid { 33 | display: grid; 34 | grid-template-columns: repeat(100, 3px); 35 | grid-template-rows: repeat(100, 3px); 36 | gap: 0; 37 | background: #000; 38 | } 39 | .cell { width: 3px; height: 3px; } 40 | .c0 { background: #111; } 41 | .c1 { background: #ffffff; } 42 | .c2 { background: #00ff00; } 43 | .c3 { background: #ffff00; } 44 | .c4 { background: #ff0000; } 45 | .legend { 46 | color: #888; 47 | font-size: 12px; 48 | margin-top: 10px; 49 | } 50 | -------------------------------------------------------------------------------- /e2e/browser-router/browser-router.test.ts-snapshots/home-page-structure.txt: -------------------------------------------------------------------------------- 1 |

Welcome to Browser App

This is an example of the new Routes-based routing system.

Interactive Counter

Count: 0


Features

  • Routes enum for declarative route definitions
  • Component ID-based resolution
  • Layout support for nested routes
  • Dynamic parameters with :id syntax
  • URLPattern API for matching

Current path: /demo/browser_router

-------------------------------------------------------------------------------- /src/platform/dom/router/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/platform/dom/router" 3 | 4 | import( 5 | "mizchi/luna/luna/routes" 6 | "mizchi/luna/luna/signal" 7 | ) 8 | 9 | // Values 10 | 11 | // Errors 12 | 13 | // Types and methods 14 | pub struct BrowserRouter { 15 | routes : Array[@routes.CompiledRoutes] 16 | base : String 17 | current_path : @signal.Signal[String] 18 | current_match : @signal.Signal[@routes.RoutesMatch?] 19 | } 20 | pub fn BrowserRouter::from_compiled(Array[@routes.CompiledRoutes], base? : String) -> Self 21 | pub fn BrowserRouter::get_base(Self) -> String 22 | pub fn BrowserRouter::get_component(Self) -> String? 23 | pub fn BrowserRouter::get_match(Self) -> @routes.RoutesMatch? 24 | pub fn BrowserRouter::get_navigate(Self) -> (String) -> Unit 25 | pub fn BrowserRouter::get_path(Self) -> String 26 | pub fn BrowserRouter::match_signal(Self) -> @signal.Signal[@routes.RoutesMatch?] 27 | pub fn BrowserRouter::navigate(Self, String) -> Unit 28 | pub fn BrowserRouter::new(Array[@routes.Routes], base? : String) -> Self 29 | pub fn BrowserRouter::path_signal(Self) -> @signal.Signal[String] 30 | pub fn BrowserRouter::replace(Self, String) -> Unit 31 | 32 | // Type aliases 33 | 34 | // Traits 35 | 36 | -------------------------------------------------------------------------------- /scripts/lib/cov-reporter/reporters/console.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Console coverage reporter 3 | */ 4 | 5 | import type { FileCoverage } from "../types.ts"; 6 | import { calculateStats, calculateDirectoryStats } from "../merge.ts"; 7 | 8 | export function reportToConsole(coverage: Map): void { 9 | const stats = calculateStats(coverage); 10 | 11 | console.log("\n" + "─".repeat(70)); 12 | console.log("📊 Unified Coverage Report"); 13 | console.log("─".repeat(70)); 14 | 15 | console.log( 16 | `\nOverall: ${stats.rate.toFixed(1)}% (${stats.coveredLines}/${stats.totalLines} lines in ${stats.files} files)` 17 | ); 18 | 19 | console.log("\n📁 Per-directory breakdown:"); 20 | 21 | const byDir = calculateDirectoryStats(coverage); 22 | const sortedDirs = [...byDir.entries()].sort((a, b) => 23 | a[0].localeCompare(b[0]) 24 | ); 25 | 26 | for (const [dir, dirStats] of sortedDirs) { 27 | const rate = 28 | dirStats.total > 0 ? (dirStats.covered / dirStats.total) * 100 : 0; 29 | const icon = rate >= 80 ? "🟢" : rate >= 50 ? "🟡" : "🔴"; 30 | console.log( 31 | ` ${icon} ${dir.padEnd(40)} ${rate.toFixed(1).padStart(5)}% (${dirStats.covered}/${dirStats.total})` 32 | ); 33 | } 34 | 35 | console.log("─".repeat(70)); 36 | } 37 | -------------------------------------------------------------------------------- /src/astra/docs/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Overview and setup guide for Luna SSG 4 | --- 5 | 6 | # Getting Started 7 | 8 | Luna SSG is a static site generator integrated into the Sol CLI. 9 | 10 | ## Prerequisites 11 | 12 | - MoonBit installed 13 | - Node.js 18+ installed 14 | 15 | ## Directory Structure 16 | 17 | ``` 18 | project/ 19 | ├── docs/ 20 | │ ├── index.md # Home page (/) 21 | │ └── guide/ 22 | │ ├── index.md # Guide index (/guide/) 23 | │ └── config.md # Config page (/guide/config) 24 | ├── sol.config.json # Configuration file 25 | └── dist/ # Output directory (auto-generated) 26 | ``` 27 | 28 | ## Basic Usage 29 | 30 | ### 1. Create Configuration 31 | 32 | Add an `ssg` section to `sol.config.json`: 33 | 34 | ```json 35 | { 36 | "ssg": { 37 | "docs": "docs", 38 | "output": "dist", 39 | "title": "My Site" 40 | } 41 | } 42 | ``` 43 | 44 | ### 2. Create Markdown Files 45 | 46 | `docs/index.md`: 47 | 48 | ```markdown 49 | --- 50 | title: Welcome 51 | --- 52 | 53 | # Hello World 54 | 55 | This is my documentation site. 56 | ``` 57 | 58 | ### 3. Build 59 | 60 | ```bash 61 | sol ssg build 62 | ``` 63 | 64 | Generated files will be output to the `dist/` directory. 65 | -------------------------------------------------------------------------------- /docs/ja/03_sol/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sol Framework 3 | --- 4 | 5 | # Sol Framework 6 | 7 | > **実験的**: Solは開発中です。APIは予告なく変更される可能性があります。 8 | 9 | SolはLuna UIとMoonBitで構築されたフルスタックSSRフレームワークです。 10 | 11 | ## 特徴 12 | 13 | - **ファイルベースルーティング** - ディレクトリ構造からページとAPIルートを生成 14 | - **Islandハイドレーション** - スマートなトリガーで最小限のJavaScriptを配信 15 | - **型安全なサーバー/クライアント** - MoonBitの型がサーバーからブラウザまで流れる 16 | - **Hono統合** - 高速で軽量なHTTPサーバー 17 | 18 | ## クイックスタート 19 | 20 | ```bash 21 | # 新しいSolプロジェクトを作成 22 | npx sol new my-app 23 | cd my-app 24 | 25 | # 開発 26 | npm run dev 27 | 28 | # 本番ビルド 29 | npm run build 30 | ``` 31 | 32 | ## プロジェクト構造 33 | 34 | ``` 35 | my-app/ 36 | ├── app/ 37 | │ ├── pages/ # ファイルベースルート 38 | │ │ ├── index.mbt # / 39 | │ │ └── about.mbt # /about 40 | │ ├── islands/ # インタラクティブコンポーネント 41 | │ │ └── counter.mbt 42 | │ └── api/ # APIルート 43 | │ └── hello.mbt # /api/hello 44 | ├── sol.config.json 45 | └── moon.mod.json 46 | ``` 47 | 48 | ## ステータス 49 | 50 | Solは実験的であり、本番環境での使用には適していません。推奨: 51 | 52 | - 静的ドキュメントサイトには[Astra](/ja/astra/)を使用 53 | - クライアントサイドアプリケーションにはLuna UIを直接使用 54 | - 更新は[GitHubリポジトリ](https://github.com/aspect-build/aspect-cli)をフォロー 55 | 56 | ## 関連 57 | 58 | - [Luna UIガイド](/ja/luna/) - コアリアクティビティの概念 59 | - [Astra SSG](/ja/astra/) - 静的サイト生成 60 | -------------------------------------------------------------------------------- /.claude/skills/mooncheat/SKILL.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: mooncheat 3 | description: Moonbit cheatsheet to check syntax and corelibrary usages 4 | allowed-tools: Read, Grep, Glob 5 | --- 6 | 7 | - See [syntax.mbt.md](./syntax.mbt.md) to check moonbit syntax 8 | - Search builtin APIs 9 | - Grep `~/.moon/lib/core` to check syntax details. 10 | - MoonDoc: `moon doc ArrayView`, `moon doc Array*` 11 | - Grep `.mooncakes/` to check library usages. 12 | - username/pkg/(src/)?pkg.generated.mbti 13 | - Configuration 14 | - moon.pkg.json 15 | - See https://docs.moonbitlang.com/en/latest/toolchain/moon/package.html 16 | - moon.pkg.json's jsonschema https://raw.githubusercontent.com/moonbitlang/moon/71abb232f9b661c079246a85a19ff8fe3421170a/crates/moonbuild/template/pkg.schema.json 17 | - moon.mod.json 18 | - See https://docs.moonbitlang.com/en/latest/toolchain/moon/module.html 19 | - moon.mod.json's jsonschema https://raw.githubusercontent.com/moonbitlang/moon/71abb232f9b661c079246a85a19ff8fe3421170a/crates/moonbuild/template/mod.schema.json 20 | - `moon check` warning and alert configuration 21 | - Get warning list by `moonc build-package -warn-help` 22 | - cross target build 23 | - [xplat-build.md](./xplat-build.md) 24 | - Writing benchmark https://docs.moonbitlang.com/en/latest/language/benchmarks.html -------------------------------------------------------------------------------- /demo-src/browser_router/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Browser App Example 7 | 48 | 49 | 50 | 51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /docs/01_luna/02_api-js/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "API: JavaScript" 3 | --- 4 | 5 | # JavaScript API Reference 6 | 7 | ## Reactive Primitives 8 | 9 | | Function | Description | 10 | |----------|-------------| 11 | | [`createSignal`](./signals#createsignal) | Create a reactive signal | 12 | | [`createEffect`](./signals#createeffect) | Create a side effect | 13 | | [`createMemo`](./signals#creatememo) | Create a cached computed value | 14 | | [`batch`](./signals#batch) | Batch multiple updates | 15 | | [`untrack`](./signals#untrack) | Run without tracking dependencies | 16 | | [`onCleanup`](./signals#oncleanup) | Register cleanup in effect | 17 | | [`onMount`](./signals#onmount) | Run code when component mounts | 18 | 19 | ## Island Hydration 20 | 21 | | Function | Description | 22 | |----------|-------------| 23 | | [`hydrate`](./islands#hydrate) | Hydrate a standard island | 24 | | [`hydrateWC`](./islands#hydratewc) | Hydrate a Web Component island | 25 | 26 | ## Control Flow Components 27 | 28 | | Component | Description | 29 | |-----------|-------------| 30 | | `Show` | Conditional rendering | 31 | | `For` | List rendering | 32 | | `Switch` / `Match` | Multi-condition rendering | 33 | 34 | ## Sections 35 | 36 | - [Signals](./signals) - Reactive state management 37 | - [Islands](./islands) - Partial hydration API 38 | -------------------------------------------------------------------------------- /src/astra/markdown/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/astra/markdown" 3 | 4 | import( 5 | "mizchi/luna/astra" 6 | "mizchi/luna/luna" 7 | ) 8 | 9 | // Values 10 | pub fn convert_cst_document(@mizchi/markdown.Document) -> (@astra.Frontmatter, Array[@astra.MdNode]) 11 | 12 | pub fn extract_description(Array[@astra.MdNode], max_length? : Int) -> String? 13 | 14 | pub fn extract_toc(Array[@astra.MdNode]) -> Array[TocItem] 15 | 16 | pub fn md_node_to_vnode(@astra.MdNode, islands_base_path? : String, current_url_path? : String, is_index? : Bool) -> @luna.Node[Unit] 17 | 18 | pub fn md_nodes_to_vnode(Array[@astra.MdNode], islands_base_path? : String, current_url_path? : String, is_index? : Bool) -> @luna.Node[Unit] 19 | 20 | pub fn parse_code_block_info(String) -> CodeBlockInfo 21 | 22 | pub fn parse_markdown(String) -> (@astra.Frontmatter, Array[@astra.MdNode]) 23 | 24 | pub fn parse_markdown_native(String) -> (@astra.Frontmatter, Array[@astra.MdNode]) 25 | 26 | // Errors 27 | 28 | // Types and methods 29 | pub(all) struct CodeBlockInfo { 30 | lang : String 31 | filename : String 32 | meta : String 33 | } 34 | 35 | pub(all) struct TocItem { 36 | level : Int 37 | text : String 38 | id : String 39 | } 40 | 41 | // Type aliases 42 | 43 | // Traits 44 | 45 | -------------------------------------------------------------------------------- /docs/00_introduction/02_getting-started/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | --- 4 | 5 | # Getting Started 6 | 7 | Get up and running with Luna in minutes. 8 | 9 | ## Choose Your Path 10 | 11 | ### JavaScript/TypeScript Developers 12 | 13 | 1. Install the package: 14 | 15 | ```bash 16 | npm install @luna_ui/luna 17 | ``` 18 | 19 | 2. Start with the [JavaScript Tutorial](/luna/tutorial-js/) 20 | 21 | ### MoonBit Developers 22 | 23 | 1. Add to your `moon.mod.json`: 24 | 25 | ```json 26 | { 27 | "deps": { 28 | "mizchi/luna": "0.1.0" 29 | } 30 | } 31 | ``` 32 | 33 | 2. Start with the [MoonBit Tutorial](/luna/tutorial-moonbit/) 34 | 35 | ## Quick Examples 36 | 37 | ### JavaScript 38 | 39 | ```typescript 40 | import { createSignal, createEffect } from '@luna_ui/luna'; 41 | 42 | const [count, setCount] = createSignal(0); 43 | createEffect(() => console.log(count())); 44 | setCount(1); // Logs: 1 45 | ``` 46 | 47 | ### MoonBit 48 | 49 | ```moonbit 50 | using @luna { signal, effect } 51 | 52 | let count = signal(0) 53 | effect(fn() { println(count.get().to_string()) }) 54 | count.set(1) // Prints: 1 55 | ``` 56 | 57 | ## Next Steps 58 | 59 | - [Luna Core](/luna/) - Learn about signals, islands, and components 60 | - [Astra](/astra/) - Build documentation sites 61 | - [Sol](/sol/) - Build full-stack applications 62 | -------------------------------------------------------------------------------- /e2e/template-app/playwright.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test"; 2 | import { dirname, join } from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | /** 8 | * Playwright config for CLI-generated template app tests 9 | * 10 | * The webServer command runs the setup script that: 11 | * 1. Generates app from templates using CLI 12 | * 2. Builds the MoonBit code 13 | * 3. Bundles the client code 14 | * 4. Starts the server on port 3000 15 | */ 16 | export default defineConfig({ 17 | testDir: __dirname, 18 | testMatch: "**/*.test.ts", 19 | fullyParallel: false, // Sequential for template app 20 | forbidOnly: !!process.env.CI, 21 | retries: process.env.CI ? 2 : 0, 22 | workers: 1, 23 | reporter: "list", 24 | use: { 25 | baseURL: "http://localhost:3000", 26 | trace: "on-first-retry", 27 | }, 28 | projects: [ 29 | { 30 | name: "chromium", 31 | use: { ...devices["Desktop Chrome"] }, 32 | }, 33 | ], 34 | webServer: { 35 | command: `bash ${join(__dirname, "../../js/cli/test-template.sh")}`, 36 | url: "http://127.0.0.1:3000", 37 | reuseExistingServer: !process.env.CI, 38 | stdout: "pipe", 39 | timeout: 120000, // 2 minutes for build + start 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /src/platform/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | プラットフォーム固有の実装。 4 | 5 | ## モジュール構成 6 | 7 | | サブモジュール | 責務 | 8 | |---------------|------| 9 | | `dom/` | ブラウザDOM操作 | 10 | | `js/` | JS固有API | 11 | | `server_dom/` | サーバーサイドDOM (イベントなし) | 12 | 13 | ## dom/ 14 | 15 | ブラウザ環境向けのDOM操作。 16 | 17 | | パス | 責務 | 18 | |-----|------| 19 | | `element/` | 低レベルDOM操作 (render, diff, reconcile) | 20 | | `client/` | クライアント側Hydration | 21 | | `router/` | クライアントサイドルーティング | 22 | | `portal/` | Portalコンポーネント | 23 | | `island.mbt` | Island Hydration | 24 | | `wc_island.mbt` | Web Components Island (Declarative Shadow DOM) | 25 | 26 | ## js/ 27 | 28 | JavaScript固有の機能。 29 | 30 | | パス | 責務 | 31 | |-----|------| 32 | | `api/` | JS向け公開API (`@luna_ui/luna`) | 33 | | `stream_renderer/` | ストリーミングSSR | 34 | | `cache/` | ファイルキャッシュ (mtime ベース) | 35 | | `fs_adapter/` | FileSystem アダプター (Node.js, memfs) | 36 | 37 | ## server_dom/ 38 | 39 | サーバーサイドDOM生成。イベントハンドラなし。 40 | 41 | | パス | 責務 | 42 | |-----|------| 43 | | `element/` | HTML要素ファクトリ (html, head, body等) | 44 | | `island.mbt` | Islandヘルパー | 45 | | `wc_island.mbt` | Web Components Island | 46 | | `render.mbt` | レンダリング関数 | 47 | 48 | ## 使い分け 49 | 50 | ``` 51 | ブラウザ実行時 → dom/ 52 | サーバー実行時 → server_dom/ 53 | JS固有機能 → js/ 54 | ``` 55 | 56 | ## 参照 57 | 58 | - [Luna Core](../luna/README.md) - VNode定義 59 | - [Stella](../stella/README.md) - Shard生成 60 | -------------------------------------------------------------------------------- /src/luna/render/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/luna/render" 3 | 4 | import( 5 | "mizchi/luna/luna" 6 | ) 7 | 8 | // Values 9 | pub fn escape_attr(String) -> String 10 | 11 | pub fn escape_attr_to(StringBuilder, String) -> Unit 12 | 13 | pub fn escape_html(String) -> String 14 | 15 | pub fn escape_html_to(StringBuilder, String) -> Unit 16 | 17 | pub fn escape_js_string(String) -> String 18 | 19 | pub fn escape_js_string_to(StringBuilder, String) -> Unit 20 | 21 | pub fn escape_json_for_attr(String) -> String 22 | 23 | pub fn escape_json_for_script(String) -> String 24 | 25 | pub fn generate_preload_tags(Array[String]) -> String 26 | 27 | pub fn is_void_element(String) -> Bool 28 | 29 | pub fn needs_attr_escape(String) -> Bool 30 | 31 | pub fn needs_html_escape(String) -> Bool 32 | 33 | pub fn render_attr_value(StringBuilder, String, String) -> Unit 34 | 35 | pub fn[E] render_attrs_to(StringBuilder, Array[(String, @luna.Attr[E])]) -> Unit 36 | 37 | pub fn[E] render_to_string(@luna.Node[E], preload? : Bool) -> SSRResult 38 | 39 | pub fn[E] render_to_string_with_hydration(@luna.Node[E]) -> String 40 | 41 | // Errors 42 | 43 | // Types and methods 44 | pub struct SSRResult { 45 | html : String 46 | preload_urls : Array[String] 47 | } 48 | 49 | // Type aliases 50 | 51 | // Traits 52 | 53 | -------------------------------------------------------------------------------- /docs/ja/05_tutorial-moonbit/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: チュートリアル (MoonBit) 3 | --- 4 | 5 | # チュートリアル (MoonBit) 6 | 7 | > TypeScript と MoonBit の両方の例を含む完全なチュートリアルは、[メインチュートリアル](/luna/tutorial-js/)を参照してください。 8 | 9 | ## MoonBit クイックスタート 10 | 11 | `moon.mod.json` に Luna を追加: 12 | 13 | ```json 14 | { 15 | "deps": { 16 | "mizchi/luna": "0.1.0" 17 | } 18 | } 19 | ``` 20 | 21 | ## 基本的なカウンター例 22 | 23 | ```moonbit 24 | using @element { 25 | div, p, button, text, text_dyn, events, 26 | type DomNode, 27 | } 28 | using @luna { signal } 29 | 30 | fn counter() -> DomNode { 31 | let count = signal(0) 32 | 33 | div([ 34 | p([text_dyn(() => "Count: " + count.get().to_string())]), 35 | button( 36 | on=events().click(_ => count.update(n => n + 1)), 37 | [text("Increment")], 38 | ), 39 | ]) 40 | } 41 | ``` 42 | 43 | ## TypeScript との違い 44 | 45 | | TypeScript | MoonBit | 46 | |------------|---------| 47 | | `createSignal(0)` | `signal(0)` | 48 | | `count()` | `count.get()` | 49 | | `setCount(5)` | `count.set(5)` | 50 | | `setCount(c => c + 1)` | `count.update(n => n + 1)` | 51 | | `createEffect(() => ...)` | `effect(() => ...)` | 52 | | `createMemo(() => ...)` | `memo(() => ...)` | 53 | 54 | ## 関連 55 | 56 | - [チュートリアル](/luna/tutorial-js/) - 全トピックの完全なチュートリアル 57 | - [Luna コア](/ja/luna/) - コア概念 58 | - [Luna API](/ja/luna/signals/) - Signals API リファレンス 59 | -------------------------------------------------------------------------------- /docs/01_luna/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Luna 3 | --- 4 | 5 | # Luna Core 6 | 7 | Luna is a reactive UI library with fine-grained reactivity and Islands Architecture. 8 | 9 | ## Core Concepts 10 | 11 | | Concept | Description | 12 | |---------|-------------| 13 | | **Signals** | Reactive state primitives | 14 | | **Effects** | Side effects that track dependencies | 15 | | **Memos** | Cached computed values | 16 | | **Islands** | Partially hydrated interactive components | 17 | 18 | ## Sections 19 | 20 | - [Why Luna](/luna/why-luna/) - Design philosophy and advantages 21 | - [API: JavaScript](/luna/api-js/) - JavaScript API reference 22 | - [API: MoonBit](/luna/api-moonbit/) - MoonBit API reference 23 | - [Tutorial: JavaScript](/luna/tutorial-js/) - Step-by-step JS guide 24 | - [Tutorial: MoonBit](/luna/tutorial-moonbit/) - Step-by-step MoonBit guide 25 | - [Deep Dive](/luna/deep-dive/) - Advanced concepts and internals 26 | 27 | ## Quick Start 28 | 29 | ### JavaScript 30 | 31 | ```typescript 32 | import { createSignal, createEffect } from '@luna_ui/luna'; 33 | 34 | const [count, setCount] = createSignal(0); 35 | createEffect(() => console.log(count())); 36 | setCount(1); 37 | ``` 38 | 39 | ### MoonBit 40 | 41 | ```moonbit 42 | using @luna { signal, effect } 43 | 44 | let count = signal(0) 45 | effect(fn() { println(count.get().to_string()) }) 46 | count.set(1) 47 | ``` 48 | -------------------------------------------------------------------------------- /experiments/view_transition/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Home - View Transition Demo 7 | 8 | 9 | 10 | 11 | 17 | 18 |
19 |

Home Page

20 |
21 |
22 |

Welcome to the View Transition API demo for MPA.

23 |

Click the navigation links to see cross-document transitions.

24 |
25 | 26 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /src/platform/dom/client/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/platform/dom/client" 3 | 4 | import( 5 | "mizchi/js/browser/dom" 6 | "mizchi/js/core" 7 | "mizchi/luna/luna" 8 | ) 9 | 10 | // Values 11 | pub fn bind_actions_from_dom(@dom.Element, (String) -> Unit) -> Unit 12 | 13 | pub fn hydrate(@dom.Element, @luna.Node[@core.Any], recover_on_mismatch? : Bool, warn_on_mismatch? : Bool, logger? : StringBuilder) -> HydrationResult 14 | 15 | pub fn[State] hydrate_with_actions(@dom.Element, State, (State) -> @luna.Node[@core.Any], (State, String) -> State) -> Unit 16 | 17 | pub fn hydration_result_mismatch(String) -> HydrationResult 18 | 19 | pub fn hydration_result_recovered(String) -> HydrationResult 20 | 21 | pub fn hydration_result_success() -> HydrationResult 22 | 23 | pub fn render_vnode(@dom.Element, @luna.Node[@core.Any]) -> Unit 24 | 25 | pub fn render_vnode_to_dom(@luna.Node[@core.Any]) -> @dom.Node 26 | 27 | // Errors 28 | 29 | // Types and methods 30 | pub enum HydrationResult { 31 | Success 32 | Mismatch(String) 33 | Recovered(String) 34 | } 35 | pub fn HydrationResult::get_message(Self) -> String 36 | pub fn HydrationResult::is_mismatch(Self) -> Bool 37 | pub fn HydrationResult::is_recovered(Self) -> Bool 38 | pub fn HydrationResult::is_success(Self) -> Bool 39 | 40 | // Type aliases 41 | 42 | // Traits 43 | 44 | -------------------------------------------------------------------------------- /src/platform/server_dom/island.mbt: -------------------------------------------------------------------------------- 1 | // Island helpers for server_dom 2 | // 3 | // Provides integration with client-side components through islands. 4 | 5 | // ============================================================================= 6 | // Type-safe client component API (Recommended) 7 | // ============================================================================= 8 | 9 | ///| 10 | /// Embed a client component with type-safe props 11 | /// 12 | /// This is the recommended way to create interactive components in server-rendered HTML. 13 | /// The ComponentRef contains all necessary information (url, props, trigger, wc flag). 14 | /// 15 | /// Example: 16 | /// ```moonbit 17 | /// // In app/__gen__/types/types.mbt (generated) 18 | /// pub fn counter(props: CounterProps) -> @luna.ComponentRef[CounterProps] { 19 | /// @luna.component_ref("/static/counter.js", props) 20 | /// } 21 | /// 22 | /// // In server component 23 | /// client( 24 | /// @types.counter({ initial_count: 42 }), 25 | /// [text("Loading...")], 26 | /// ) 27 | /// ``` 28 | pub fn[T : ToJson] client( 29 | cref : @luna.ComponentRef[T], 30 | children : Array[@luna.Node[Unit]], 31 | styles? : String = "", 32 | ) -> @luna.Node[Unit] { 33 | @luna.internal_ref( 34 | cref.url, 35 | cref.props.to_json().stringify(), 36 | trigger=cref.trigger, 37 | wc=cref.wc, 38 | styles~, 39 | children~, 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /src/astra/shiki/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/astra/shiki" 3 | 4 | import( 5 | "mizchi/js/core" 6 | ) 7 | 8 | // Values 9 | pub fn create_default_highlighter() -> @core.Promise[Highlighter] 10 | 11 | pub fn create_highlighter_async(Array[String], Array[String]) -> @core.Promise[Highlighter] 12 | 13 | pub fn decode_html_entities(String) -> String 14 | 15 | pub fn generate_shiki_css() -> String 16 | 17 | pub fn is_language_supported(String) -> Bool 18 | 19 | pub fn resolve_language(String) -> String 20 | 21 | // Errors 22 | 23 | // Types and methods 24 | #external 25 | pub type Highlighter 26 | pub fn Highlighter::as_any(Self) -> @core.Any 27 | pub fn Highlighter::code_to_html(Self, String, lang~ : String, theme~ : String) -> String 28 | pub fn Highlighter::code_to_html_dual(Self, String, lang~ : String, light_theme? : String, dark_theme? : String) -> String 29 | pub fn Highlighter::ensure_language_async(Self, String) -> @core.Promise[Unit] 30 | pub fn Highlighter::get_loaded_languages(Self) -> Array[String] 31 | pub fn Highlighter::get_loaded_themes(Self) -> Array[String] 32 | pub fn Highlighter::highlight_async(Self, String, String) -> @core.Promise[String] 33 | pub fn Highlighter::is_language_loaded(Self, String) -> Bool 34 | pub fn Highlighter::load_language_async(Self, String) -> @core.Promise[Unit] 35 | 36 | // Type aliases 37 | 38 | // Traits 39 | 40 | -------------------------------------------------------------------------------- /scripts/lib/cov-reporter/sourcemap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Source map loading utilities 3 | */ 4 | 5 | import { readFile, readdir } from "node:fs/promises"; 6 | import { existsSync } from "node:fs"; 7 | import { join } from "node:path"; 8 | import { SourceMapConsumer } from "source-map"; 9 | 10 | export async function loadSourceMaps( 11 | sourceMapDir: string 12 | ): Promise> { 13 | const maps = new Map(); 14 | 15 | if (!existsSync(sourceMapDir)) { 16 | return maps; 17 | } 18 | 19 | async function scanDir(dir: string) { 20 | const entries = await readdir(dir, { withFileTypes: true }); 21 | for (const entry of entries) { 22 | const fullPath = join(dir, entry.name); 23 | if (entry.isDirectory()) { 24 | await scanDir(fullPath); 25 | } else if (entry.name.endsWith(".js.map")) { 26 | const content = await readFile(fullPath, "utf-8"); 27 | const rawMap = JSON.parse(content); 28 | const consumer = await new SourceMapConsumer(rawMap); 29 | const jsFile = fullPath.replace(".map", ""); 30 | maps.set(jsFile, consumer); 31 | } 32 | } 33 | } 34 | 35 | await scanDir(sourceMapDir); 36 | return maps; 37 | } 38 | 39 | export function destroySourceMaps( 40 | maps: Map 41 | ): void { 42 | for (const sm of maps.values()) { 43 | sm.destroy(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/sol/isr/memory_cache.mbt: -------------------------------------------------------------------------------- 1 | // Sol ISR Memory Cache Implementation 2 | // 3 | // In-memory cache for development and testing. 4 | // Not suitable for production (data lost on restart). 5 | 6 | // ============================================================================ 7 | // MemoryCache 8 | // ============================================================================ 9 | 10 | ///| 11 | /// In-memory ISR cache implementation 12 | /// Suitable for development and single-instance deployments 13 | pub struct MemoryCache { 14 | store : Map[String, CacheEntry] 15 | } 16 | 17 | ///| 18 | /// Create a new empty MemoryCache 19 | pub fn MemoryCache::new() -> MemoryCache { 20 | { store: {} } 21 | } 22 | 23 | ///| 24 | pub impl ISRCache for MemoryCache with get(self, key) { 25 | self.store.get(key) 26 | } 27 | 28 | ///| 29 | pub impl ISRCache for MemoryCache with put(self, key, entry) { 30 | self.store[key] = entry 31 | } 32 | 33 | ///| 34 | pub impl ISRCache for MemoryCache with delete(self, key) { 35 | self.store.remove(key) |> ignore 36 | } 37 | 38 | ///| 39 | pub impl ISRCache for MemoryCache with schedule_revalidation(_self, task) { 40 | // In memory mode, execute task synchronously via promise 41 | // This is simplified - in production, use waitUntil 42 | let _ = ffi_run_async(task) 43 | 44 | } 45 | 46 | ///| 47 | extern "js" fn ffi_run_async(task : async () -> Unit) -> @js.Promise[Unit] = 48 | #| (task) => task() 49 | -------------------------------------------------------------------------------- /docs/01_luna/03_api-moonbit/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "API: MoonBit" 3 | --- 4 | 5 | # MoonBit API Reference 6 | 7 | ## Reactive Primitives 8 | 9 | | Function | Description | 10 | |----------|-------------| 11 | | [`signal`](./signals#signal) | Create a reactive signal | 12 | | [`effect`](./signals#effect) | Create a side effect | 13 | | [`memo`](./signals#memo) | Create a cached computed value | 14 | | [`batch`](./signals#batch) | Batch multiple updates | 15 | | [`untrack`](./signals#untrack) | Run without tracking dependencies | 16 | | [`on_cleanup`](./signals#on_cleanup) | Register cleanup in effect | 17 | 18 | ## Island Rendering 19 | 20 | | Function | Description | 21 | |----------|-------------| 22 | | [`island`](./islands#island) | Create an island for hydration | 23 | | [`wc_island`](./islands#wc_island) | Create a Web Component island | 24 | 25 | ## Server DOM Elements 26 | 27 | | Function | Description | 28 | |----------|-------------| 29 | | [`div`, `p`, `span`, ...](./render#elements) | HTML element factories | 30 | | [`text`](./render#text) | Create text node | 31 | | [`attr`](./render#attr) | Create attribute | 32 | 33 | ## Rendering 34 | 35 | | Function | Description | 36 | |----------|-------------| 37 | | [`render_to_string`](./render#render_to_string) | Render VNode to HTML string | 38 | 39 | ## Sections 40 | 41 | - [Signals](./signals) - Reactive state management 42 | - [Islands](./islands) - Server-side island rendering 43 | - [Render](./render) - HTML rendering utilities 44 | -------------------------------------------------------------------------------- /src/platform/server_dom/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/platform/server_dom" 3 | 4 | import( 5 | "mizchi/luna/luna" 6 | ) 7 | 8 | // Values 9 | pub fn[T : ToJson] client(@luna.ComponentRef[T], Array[@luna.Node[Unit]], styles? : String) -> @luna.Node[Unit] 10 | 11 | pub fn document(lang? : String, head_children~ : Array[@luna.Node[Unit]], body_children~ : Array[@luna.Node[Unit]], body_class? : String, body_id? : String) -> @luna.Node[Unit] 12 | 13 | pub fn generate_preload_tags(Array[String]) -> String 14 | 15 | pub fn render(@luna.Node[Unit]) -> String 16 | 17 | pub fn render_document(@luna.Node[Unit]) -> String 18 | 19 | pub fn render_document_with_preloads(@luna.Node[Unit]) -> RenderResult 20 | 21 | pub fn render_with_preloads(@luna.Node[Unit]) -> RenderResult 22 | 23 | pub fn wc_island(String, String, Array[@luna.Node[Unit]], styles? : String, state? : String, trigger? : @luna.TriggerType) -> @luna.Node[Unit] 24 | 25 | // Errors 26 | 27 | // Types and methods 28 | pub struct RenderResult { 29 | html : String 30 | preload_urls : Array[String] 31 | } 32 | 33 | pub enum ServerNode { 34 | Sync(@luna.Node[Unit]) 35 | Async(async () -> @luna.Node[Unit]) 36 | } 37 | pub fn ServerNode::async_(async () -> @luna.Node[Unit]) -> Self 38 | pub fn ServerNode::is_async(Self) -> Bool 39 | pub async fn ServerNode::resolve(Self) -> @luna.Node[Unit] 40 | pub fn ServerNode::sync(@luna.Node[Unit]) -> Self 41 | 42 | // Type aliases 43 | 44 | // Traits 45 | 46 | -------------------------------------------------------------------------------- /docs/ja/06_tutorial-js/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: チュートリアル 3 | --- 4 | 5 | # チュートリアル 6 | 7 | JavaScript/TypeScript で Luna UI を学びます。各レッスンには TypeScript と MoonBit の両方の例が含まれています。 8 | 9 | ## はじめに 10 | 11 | ```bash 12 | npm install @luna_ui/luna 13 | ``` 14 | 15 | ## イントロダクション 16 | 17 | - [Basics](/luna/tutorial-js/introduction_basics/) - 最初のリアクティブコンポーネント 18 | - [Signals](/luna/tutorial-js/introduction_signals/) - リアクティブな状態コンテナ 19 | - [Effects](/luna/tutorial-js/introduction_effects/) - 副作用とサブスクリプション 20 | - [Memos](/luna/tutorial-js/introduction_memos/) - 算出値/派生値 21 | 22 | ## リアクティビティ 23 | 24 | - [Batch](/luna/tutorial-js/reactivity_batch/) - 複数更新のバッチ処理 25 | - [Untrack](/luna/tutorial-js/reactivity_untrack/) - 追跡なしで読み取り 26 | - [Nested Effects](/luna/tutorial-js/reactivity_nested/) - Effect の合成 27 | 28 | ## 制御フロー 29 | 30 | - [Show](/luna/tutorial-js/flow_show/) - 条件付きレンダリング 31 | - [For](/luna/tutorial-js/flow_for/) - リストレンダリング 32 | - [Switch](/luna/tutorial-js/flow_switch/) - 複数条件分岐 33 | 34 | ## ライフサイクル 35 | 36 | - [onMount](/luna/tutorial-js/lifecycle_onmount/) - マウント時コールバック 37 | - [onCleanup](/luna/tutorial-js/lifecycle_oncleanup/) - クリーンアップと破棄 38 | 39 | ## Islands 40 | 41 | - [Basics](/luna/tutorial-js/islands_basics/) - Island アーキテクチャの基礎 42 | - [State](/luna/tutorial-js/islands_state/) - Island での状態管理 43 | - [Triggers](/luna/tutorial-js/islands_triggers/) - ハイドレーショントリガー 44 | - [Web Components](/luna/tutorial-js/islands_webcomponents/) - カスタム要素との統合 45 | 46 | ## 関連 47 | 48 | - [Luna コア](/ja/luna/) - コア概念と API 49 | - [Astra SSG](/ja/astra/) - 静的サイト生成 50 | -------------------------------------------------------------------------------- /src/tests/json_renderer/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/tests/json_renderer" 3 | 4 | import( 5 | "mizchi/luna/luna" 6 | ) 7 | 8 | // Values 9 | pub fn attr_dynamic_value(String) -> JsonAttrValue 10 | 11 | pub fn attr_handler_value() -> JsonAttrValue 12 | 13 | pub fn attr_static(String) -> JsonAttrValue 14 | 15 | pub fn json_element(String, Map[String, JsonAttrValue], Array[JsonNode]) -> JsonNode 16 | 17 | pub fn json_fragment(Array[JsonNode]) -> JsonNode 18 | 19 | pub fn json_node_to_string(JsonNode) -> String 20 | 21 | pub fn json_text(String) -> JsonNode 22 | 23 | pub fn render_to_json(@luna.Node[Unit]) -> JsonNode 24 | 25 | pub fn render_to_json_string(@luna.Node[Unit]) -> String 26 | 27 | // Errors 28 | 29 | // Types and methods 30 | pub enum JsonAttrValue { 31 | Static(String) 32 | Dynamic(String) 33 | Handler 34 | } 35 | pub impl Eq for JsonAttrValue 36 | pub impl Show for JsonAttrValue 37 | 38 | pub struct JsonElement { 39 | tag : String 40 | attrs : Map[String, JsonAttrValue] 41 | children : Array[JsonNode] 42 | } 43 | pub impl Eq for JsonElement 44 | pub impl Show for JsonElement 45 | 46 | pub enum JsonNode { 47 | Element(JsonElement) 48 | Text(String) 49 | Fragment(Array[JsonNode]) 50 | } 51 | pub fn JsonNode::children(Self) -> Array[Self] 52 | pub fn JsonNode::get_attr(Self, String) -> JsonAttrValue? 53 | pub fn JsonNode::tag(Self) -> String? 54 | pub impl Eq for JsonNode 55 | pub impl Show for JsonNode 56 | 57 | // Type aliases 58 | 59 | // Traits 60 | 61 | -------------------------------------------------------------------------------- /docs/internal/ja/introduce-luna-mbt.md: -------------------------------------------------------------------------------- 1 | ## luna 2 | 3 | MoonBit (月餅)に対して、その開発者の一人の yoorkin 氏が作ってた UI ライブラリで rabbit-tea だったので、月っぽい名前を選びました。 4 | 5 | - react 6 | - ランタイムサイズがデカすぎる 7 | - qwik 8 | - pros: 最適化 9 | - const: コンパイラの挙動が難しすぎる 10 | - preact 11 | - pros 12 | - const 13 | - svelte 14 | - pros: 軽量 15 | - const: 独自拡張子で tsx ではないのが微妙 16 | 17 | というわけで、qwik の resume とアイランドアーキテクチャを備えた、 preact のような専用のコア 18 | 19 | 20 | qwik は難しすぎる 21 | 22 | preact の signal 23 | 24 | Moonbit が流行るにはキラーアプリが必要。 25 | 26 | UIライブラリ 27 | 28 | 29 | 30 | ```mbt 31 | ///| 32 | fn counter_example() -> @dom.DomNode { 33 | let count = @signal.signal(0) 34 | let doubled = @signal.memo(fn() { count.get() * 2 }) 35 | let is_even = @signal.memo(fn() { count.get() % 2 == 0 }) 36 | @dom.div(class="counter-example", [ 37 | @dom.h2([@dom.text("Counter Example")]), 38 | @dom.p([@dom.text_dyn(fn() { "Count: " + count.get().to_string() })]), 39 | @dom.p([@dom.text_dyn(fn() { "Doubled: " + doubled().to_string() })]), 40 | @dom.p([@dom.text_dyn(fn() { if is_even() { "Even" } else { "Odd" } })]), 41 | @dom.div(class="buttons", [ 42 | @dom.button( 43 | on=@dom.events().click(fn(_) { count.update(fn(n) { n - 1 }) }), 44 | [@dom.text("-")], 45 | ), 46 | @dom.button( 47 | on=@dom.events().click(fn(_) { count.update(fn(n) { n + 1 }) }), 48 | [@dom.text("+")], 49 | ), 50 | @dom.button( 51 | on=@dom.events().click(fn(_) { count.set(0) }), 52 | [@dom.text("Reset")], 53 | ), 54 | ]), 55 | ]) 56 | } 57 | ``` -------------------------------------------------------------------------------- /src/platform/server_dom/element/outlet.mbt: -------------------------------------------------------------------------------- 1 | // Sol Outlet - Partial update region marker 2 | // 3 | // Creates a div element with data-sol-outlet attribute 4 | // to mark regions for partial DOM updates during CSR navigation. 5 | // Mark a region for partial updates during CSR navigation 6 | // 7 | // Renders as `
` which is used by 8 | // 9 | 10 | ///| 11 | /// sol-nav.js to replace content during navigation. 12 | pub fn outlet( 13 | name~ : String, 14 | children : Array[@luna.Node[Unit]], 15 | id? : String, 16 | class? : String, 17 | style? : String, 18 | attrs? : Array[(String, @luna.Attr[Unit])], 19 | ) -> @luna.Node[Unit] { 20 | let props = build_attrs(id, class, style, attrs) 21 | props.push(("data-sol-outlet", static_attr(name))) 22 | create_element("div", props, children) 23 | } 24 | 25 | ///| 26 | /// Create a template element for fragment response 27 | /// Used internally to wrap outlet content in fragment responses. 28 | pub fn template_outlet( 29 | name~ : String, 30 | children : Array[@luna.Node[Unit]], 31 | ) -> @luna.Node[Unit] { 32 | let props : Array[(String, @luna.Attr[Unit])] = [] 33 | props.push(("data-sol-outlet", static_attr(name))) 34 | create_element("template", props, children) 35 | } 36 | 37 | ///| 38 | /// Create a template element for page title in fragment response 39 | pub fn template_title(title : String) -> @luna.Node[Unit] { 40 | let props : Array[(String, @luna.Attr[Unit])] = [] 41 | props.push(("data-sol-title", static_attr(""))) 42 | create_element("template", props, [text(title)]) 43 | } 44 | -------------------------------------------------------------------------------- /src/stella/README.md: -------------------------------------------------------------------------------- 1 | # Stella 2 | 3 | Island埋め込み用のShard生成モジュール。 4 | 5 | ## 概要 6 | 7 | Shard は Island Architecture における hydration 可能なHTML断片。 8 | `luna:*` 属性でマークアップされ、ローダーがクライアントで検知・hydrate する。 9 | 10 | ## ファイル構成 11 | 12 | | ファイル | 責務 | 13 | |---------|------| 14 | | `types.mbt` | Shard設定・出力の型定義 | 15 | | `html_builder.mbt` | HTML生成ユーティリティ | 16 | | `serializer.mbt` | JSON/HTMLエスケープ処理 | 17 | 18 | ## Shard出力例 19 | 20 | ```html 21 |
25 | 26 |
27 | ``` 28 | 29 | ## 主要な型 30 | 31 | ### ShardConfig 32 | 33 | ```moonbit 34 | pub struct ShardConfig { 35 | id : String // コンポーネントID (luna:id) 36 | script_url : String // Hydrationスクリプト (luna:url) 37 | trigger : TriggerType // トリガー (luna:client-trigger) 38 | state : StateConfig // 状態設定 39 | ssr_content : String? // SSR済みHTML 40 | include_loader : Bool // ローダー埋め込み 41 | loader_url : String // ローダーURL 42 | } 43 | ``` 44 | 45 | ### StateConfig 46 | 47 | ```moonbit 48 | pub enum StateConfig { 49 | Empty // 状態なし 50 | Inline(String) // luna:state属性に埋め込み 51 | ScriptRef(String) // ` はエスケープ済み 60 | - HTML属性値は適切にクォート 61 | 62 | ## 参照 63 | 64 | - [Luna Core](../luna/README.md) - TriggerType の定義 65 | - [Sol](../sol/README.md) - Island統合の実装 66 | -------------------------------------------------------------------------------- /js/astra/assets/scripts/toc.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var observer = null; 3 | var tocLinks = []; 4 | var headings = []; 5 | function initTocSpy() { 6 | if (observer) observer.disconnect(); 7 | tocLinks = Array.from(document.querySelectorAll('.toc-item a')); 8 | headings = tocLinks.map(function(link) { 9 | var id = link.getAttribute('href')?.slice(1); 10 | return id ? document.getElementById(id) : null; 11 | }).filter(Boolean); 12 | if (!headings.length) return; 13 | var current = null; 14 | observer = new IntersectionObserver(function(entries) { 15 | entries.forEach(function(entry) { 16 | if (entry.isIntersecting) current = entry.target; 17 | }); 18 | if (!current) { 19 | var scrollY = window.scrollY + 100; 20 | for (var i = headings.length - 1; i >= 0; i--) { 21 | if (headings[i].offsetTop <= scrollY) { current = headings[i]; break; } 22 | } 23 | } 24 | tocLinks.forEach(function(link) { 25 | var id = link.getAttribute('href')?.slice(1); 26 | if (current && id === current.id) { 27 | link.classList.add('active'); 28 | } else { 29 | link.classList.remove('active'); 30 | } 31 | }); 32 | }, { rootMargin: '-60px 0px -70% 0px', threshold: 0 }); 33 | headings.forEach(function(h) { observer.observe(h); }); 34 | } 35 | if (document.readyState === 'loading') { 36 | document.addEventListener('DOMContentLoaded', initTocSpy); 37 | } else { 38 | initTocSpy(); 39 | } 40 | window.__ASTRA_INIT_TOC_SPY__ = initTocSpy; 41 | })(); 42 | -------------------------------------------------------------------------------- /src/astra/assets/scripts/toc.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var observer = null; 3 | var tocLinks = []; 4 | var headings = []; 5 | function initTocSpy() { 6 | if (observer) observer.disconnect(); 7 | tocLinks = Array.from(document.querySelectorAll('.toc-item a')); 8 | headings = tocLinks.map(function(link) { 9 | var id = link.getAttribute('href')?.slice(1); 10 | return id ? document.getElementById(id) : null; 11 | }).filter(Boolean); 12 | if (!headings.length) return; 13 | var current = null; 14 | observer = new IntersectionObserver(function(entries) { 15 | entries.forEach(function(entry) { 16 | if (entry.isIntersecting) current = entry.target; 17 | }); 18 | if (!current) { 19 | var scrollY = window.scrollY + 100; 20 | for (var i = headings.length - 1; i >= 0; i--) { 21 | if (headings[i].offsetTop <= scrollY) { current = headings[i]; break; } 22 | } 23 | } 24 | tocLinks.forEach(function(link) { 25 | var id = link.getAttribute('href')?.slice(1); 26 | if (current && id === current.id) { 27 | link.classList.add('active'); 28 | } else { 29 | link.classList.remove('active'); 30 | } 31 | }); 32 | }, { rootMargin: '-60px 0px -70% 0px', threshold: 0 }); 33 | headings.forEach(function(h) { observer.observe(h); }); 34 | } 35 | if (document.readyState === 'loading') { 36 | document.addEventListener('DOMContentLoaded', initTocSpy); 37 | } else { 38 | initTocSpy(); 39 | } 40 | window.__ASTRA_INIT_TOC_SPY__ = initTocSpy; 41 | })(); 42 | -------------------------------------------------------------------------------- /js/loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@luna_ui/luna-loader", 3 | "version": "0.1.0", 4 | "description": "Island loader for Luna SSR hydration", 5 | "type": "module", 6 | "main": "./dist/loader.js", 7 | "exports": { 8 | ".": "./dist/loader.js", 9 | "./boot": "./dist/boot/index.js", 10 | "./boot/loader": "./dist/boot/loader.js", 11 | "./boot/router": "./dist/boot/router.js", 12 | "./router": "./dist/router/index.js", 13 | "./router/hybrid": "./dist/router/hybrid.js", 14 | "./router/spa": "./dist/router/spa.js", 15 | "./router/scroll": "./dist/router/scroll.js", 16 | "./wc": "./dist/wc-loader.js", 17 | "./sol-nav": "./dist/sol-nav.js", 18 | "./lib": "./dist/lib.js", 19 | "./src/loader": "./src/loader.ts", 20 | "./src/boot": "./src/boot/index.ts", 21 | "./src/boot/loader": "./src/boot/loader.ts", 22 | "./src/boot/router": "./src/boot/router.ts", 23 | "./src/router": "./src/router/index.ts", 24 | "./src/router/hybrid": "./src/router/hybrid.ts", 25 | "./src/router/spa": "./src/router/spa.ts", 26 | "./src/router/scroll": "./src/router/scroll.ts", 27 | "./src/wc-loader": "./src/wc-loader.ts", 28 | "./src/sol-nav": "./src/sol-nav.ts", 29 | "./src/lib": "./src/lib.ts" 30 | }, 31 | "files": [ 32 | "dist", 33 | "src" 34 | ], 35 | "scripts": { 36 | "build": "rolldown -c rolldown.config.mjs" 37 | }, 38 | "keywords": [ 39 | "luna", 40 | "moonbit", 41 | "island", 42 | "ssr", 43 | "hydration", 44 | "web-components" 45 | ], 46 | "author": "mizchi", 47 | "license": "MIT" 48 | } 49 | -------------------------------------------------------------------------------- /e2e/wc_counter_csr.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | const DEBUG = process.env.DEBUG === '1'; 4 | 5 | test.describe('wc-counter CSR navigation', () => { 6 | test('wc-counter should work after CSR navigation', async ({ page }) => { 7 | // ホームページから開始(baseURLを使用) 8 | await page.goto('/'); 9 | 10 | // コンソールログを収集 11 | const logs: string[] = []; 12 | page.on('console', msg => { 13 | logs.push(`[${msg.type()}] ${msg.text()}`); 14 | }); 15 | 16 | // ページが読み込まれたことを確認 17 | await expect(page.locator('nav')).toBeVisible(); 18 | 19 | // WC Counterリンクをクリック(CSR遷移) 20 | await page.click('a[href="/wc-counter"]'); 21 | 22 | // 少し待つ 23 | await page.waitForTimeout(500); 24 | 25 | // URLが変わったことを確認 26 | expect(page.url()).toContain('/wc-counter'); 27 | 28 | // wc-counter要素が表示されるか確認 29 | const wcCounter = page.locator('wc-counter'); 30 | await expect(wcCounter).toBeVisible({ timeout: 5000 }); 31 | 32 | // Shadow DOM内のコンテンツを確認 33 | const countDisplay = wcCounter.locator('.count-display'); 34 | 35 | // カウント表示が0であることを確認 36 | await expect(countDisplay).toHaveText('0'); 37 | 38 | // インクリメントボタンをクリック 39 | const incButton = wcCounter.locator('.inc'); 40 | await incButton.click(); 41 | 42 | // カウントが1になることを確認 43 | await expect(countDisplay).toHaveText('1'); 44 | 45 | if (DEBUG) { 46 | console.log('Console logs:', logs); 47 | const isHydrated = logs.some(log => log.includes('[wc-counter] Hydrated')); 48 | console.log('Is hydrated:', isHydrated); 49 | } 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/sol/cli/moon.pkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "is-main": true, 3 | "supported-targets": [ 4 | "js" 5 | ], 6 | "import": [ 7 | "moonbitlang/async", 8 | { 9 | "path": "mizchi/js/web/console", 10 | "alias": "console" 11 | }, 12 | { 13 | "path": "mizchi/luna/internal/json_utils", 14 | "alias": "json_utils" 15 | }, 16 | { 17 | "path": "mizchi/luna/astra", 18 | "alias": "astra" 19 | }, 20 | { 21 | "path": "mizchi/luna/astra/generator", 22 | "alias": "ssg_gen" 23 | }, 24 | { 25 | "path": "mizchi/luna/core/ssg", 26 | "alias": "ssg" 27 | }, 28 | { 29 | "path": "mizchi/luna/core/env", 30 | "alias": "env" 31 | }, 32 | { 33 | "path": "mizchi/luna/platform/js/fs_adapter", 34 | "alias": "fs_adapter" 35 | }, 36 | { 37 | "path": "mizchi/js/core", 38 | "alias": "js" 39 | }, 40 | { 41 | "path": "mizchi/js/node/process", 42 | "alias": "process" 43 | }, 44 | { 45 | "path": "mizchi/js/node/fs", 46 | "alias": "fs" 47 | }, 48 | { 49 | "path": "mizchi/js/node/path", 50 | "alias": "path" 51 | }, 52 | { 53 | "path": "mizchi/js/node/child_process", 54 | "alias": "child_process" 55 | }, 56 | { 57 | "path": "mizchi/js/node/util", 58 | "alias": "util" 59 | }, 60 | { 61 | "path": "mizchi/npm_typed/colorette", 62 | "alias": "colorette" 63 | }, 64 | { 65 | "path": "mizchi/luna/core/parser", 66 | "alias": "parser" 67 | } 68 | ], 69 | "warn-list": "-27-20" 70 | } -------------------------------------------------------------------------------- /docs/01_luna/04_tutorial-js/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tutorial: JavaScript" 3 | --- 4 | 5 | # JavaScript Tutorial 6 | 7 | Learn Luna UI with JavaScript/TypeScript. 8 | 9 | ## Getting Started 10 | 11 | ```bash 12 | npm install @luna_ui/luna 13 | ``` 14 | 15 | ## Introduction 16 | 17 | - [Basics](./introduction_basics/) - Your first reactive component 18 | - [Signals](./introduction_signals/) - Reactive state containers 19 | - [Effects](./introduction_effects/) - Side effects and subscriptions 20 | - [Memos](./introduction_memos/) - Computed/derived values 21 | 22 | ## Reactivity 23 | 24 | - [Batch](./reactivity_batch/) - Batching multiple updates 25 | - [Untrack](./reactivity_untrack/) - Reading without tracking 26 | - [Nested Effects](./reactivity_nested/) - Effect composition 27 | 28 | ## Control Flow 29 | 30 | - [Show](./flow_show/) - Conditional rendering 31 | - [For](./flow_for/) - List rendering 32 | - [Switch](./flow_switch/) - Multi-way branching 33 | 34 | ## Lifecycle 35 | 36 | - [onMount](./lifecycle_onmount/) - Component mount callback 37 | - [onCleanup](./lifecycle_oncleanup/) - Cleanup and disposal 38 | 39 | ## Islands 40 | 41 | - [Basics](./islands_basics/) - Island architecture fundamentals 42 | - [State](./islands_state/) - State management in islands 43 | - [Triggers](./islands_triggers/) - Hydration triggers 44 | - [Web Components](./islands_webcomponents/) - Custom elements integration 45 | 46 | ## See Also 47 | 48 | - [JavaScript API Reference](/luna/api-js/) - API Reference 49 | - [MoonBit Tutorial](/luna/tutorial-moonbit/) - MoonBit version 50 | - [Astra SSG](/astra/) - Static site generation 51 | -------------------------------------------------------------------------------- /src/astra/README.md: -------------------------------------------------------------------------------- 1 | # Astra 2 | 3 | 静的サイトジェネレーター (SSG)。Markdown → HTML 変換。 4 | 5 | ## 概要 6 | 7 | ドキュメントサイト生成に特化したSSG。 8 | Markdownファイルから多言語対応の静的サイトを生成する。 9 | 10 | ## モジュール構成 11 | 12 | | サブモジュール | 責務 | 13 | |---------------|------| 14 | | `cli/` | CLIエントリポイント | 15 | | `generator/` | HTML生成ロジック | 16 | | `markdown/` | Markdown解析 | 17 | | `routes/` | ルート生成 | 18 | | `shiki/` | シンタックスハイライト (Shiki統合) | 19 | | `config.mbt` | 設定パーサー | 20 | | `types.mbt` | 型定義 | 21 | 22 | ## 設定ファイル (astra.json) 23 | 24 | ```json 25 | { 26 | "docs": "docs", 27 | "output": "dist", 28 | "title": "My Site", 29 | "base": "/", 30 | "trailingSlash": true, 31 | "i18n": { 32 | "defaultLocale": "en", 33 | "locales": [ 34 | { "code": "en", "label": "English", "path": "" }, 35 | { "code": "ja", "label": "日本語", "path": "ja" } 36 | ] 37 | }, 38 | "nav": [...], 39 | "sidebar": "auto" 40 | } 41 | ``` 42 | 43 | ## 主要な型 44 | 45 | ### SsgConfig 46 | 47 | ```moonbit 48 | pub struct SsgConfig { 49 | docs_dir : String // ソースディレクトリ 50 | output_dir : String // 出力ディレクトリ 51 | title : String // サイトタイトル 52 | base_url : String // ベースURL 53 | nav : Array[NavItem] // ナビゲーション 54 | sidebar : SidebarConfig // サイドバー設定 55 | i18n : I18nConfig // 多言語設定 56 | // ... 57 | } 58 | ``` 59 | 60 | ## 機能 61 | 62 | - Markdownからの静的HTML生成 63 | - 多言語サポート (i18n) 64 | - 自動サイドバー生成 65 | - シンタックスハイライト (Shiki) 66 | - OGP対応 67 | 68 | ## 使用方法 69 | 70 | ```bash 71 | # CLIから実行 72 | moon run src/astra/cli -- build 73 | ``` 74 | 75 | ## 参照 76 | 77 | - [Luna Core](../core/README.md) - VNode生成 78 | - [Sol](../sol/README.md) - SSRフレームワーク 79 | -------------------------------------------------------------------------------- /examples/sol_app/app/__gen__/types/types.mbt: -------------------------------------------------------------------------------- 1 | ///| Auto-generated types for island props 2 | ///| Re-exports Props structs from client packages 3 | 4 | ///| Props type from @client 5 | pub(all) struct CounterProps { 6 | initial_count : Int 7 | } derive(ToJson, FromJson) 8 | 9 | ///| Props type from @client 10 | pub(all) struct ContactFormProps { 11 | action : String 12 | initial_name : String 13 | initial_email : String 14 | } derive(ToJson, FromJson) 15 | 16 | ///| Props type from @client 17 | pub(all) struct WcCounterProps { 18 | initial_count : Int 19 | } derive(ToJson, FromJson) 20 | 21 | // ============================================================================= 22 | // ComponentRef Factory Functions 23 | // ============================================================================= 24 | 25 | ///| 26 | ///| Create ComponentRef for counter component 27 | pub fn counter( 28 | props : CounterProps, 29 | trigger~ : @luna.Trigger = @luna.Load, 30 | ) -> @luna.ComponentRef[CounterProps] { 31 | { url: "/static/counter.js", props, wc: false, trigger } 32 | } 33 | 34 | ///| 35 | ///| Create ComponentRef for contact_form component 36 | pub fn contact_form( 37 | props : ContactFormProps, 38 | trigger~ : @luna.Trigger = @luna.Load, 39 | ) -> @luna.ComponentRef[ContactFormProps] { 40 | { url: "/static/contact_form.js", props, wc: false, trigger } 41 | } 42 | 43 | ///| 44 | ///| Create ComponentRef for wc_counter component 45 | pub fn wc_counter( 46 | props : WcCounterProps, 47 | trigger~ : @luna.Trigger = @luna.Load, 48 | ) -> @luna.ComponentRef[WcCounterProps] { 49 | { url: "/static/wc_counter.js", props, wc: true, trigger } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /experiments/view_transition/bar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bar - View Transition Demo 7 | 8 | 9 | 10 | 11 | 17 | 18 |
19 |

Bar Page

20 |
21 |
22 |

This is the Bar page.

23 |

Try navigating between pages to see the transition effects.

24 |
25 | 26 |
27 |

Bar Features

28 |
    29 |
  • Smooth page transitions
  • 30 |
  • Individual element animations
  • 31 |
  • Works with browser back/forward
  • 32 |
33 |
34 | 35 | 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /scripts/lib/cov-reporter/parsers/moonbit.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * MoonBit Cobertura coverage parser 3 | */ 4 | 5 | import { readFile } from "node:fs/promises"; 6 | import { existsSync } from "node:fs"; 7 | import { join } from "node:path"; 8 | import type { FileCoverage, LineCoverage, CoverageConfig } from "../types.ts"; 9 | import { shouldExcludeFile } from "../types.ts"; 10 | 11 | export async function parseMoonbitCoverage( 12 | config: CoverageConfig 13 | ): Promise> { 14 | const coberturaFile = join(config.coverageDir, "moonbit-coverage.xml"); 15 | if (!existsSync(coberturaFile)) { 16 | return new Map(); 17 | } 18 | 19 | const content = await readFile(coberturaFile, "utf-8"); 20 | const files = new Map(); 21 | 22 | const classRegex = /]*filename="([^"]+)"[^>]*>([\s\S]*?)<\/class>/g; 23 | let classMatch; 24 | 25 | while ((classMatch = classRegex.exec(content)) !== null) { 26 | const filename = classMatch[1]; 27 | const classContent = classMatch[2]; 28 | 29 | if (config.include && !config.include.test(filename)) continue; 30 | if (shouldExcludeFile(filename, config)) continue; 31 | 32 | const lines: LineCoverage[] = []; 33 | 34 | const lineRegex = /]*\/>/g; 35 | let lineMatch; 36 | while ((lineMatch = lineRegex.exec(classContent)) !== null) { 37 | lines.push({ 38 | line: parseInt(lineMatch[1]), 39 | count: parseInt(lineMatch[2]), 40 | }); 41 | } 42 | 43 | if (lines.length > 0) { 44 | files.set(filename, { path: filename, lines }); 45 | } 46 | } 47 | 48 | return files; 49 | } 50 | -------------------------------------------------------------------------------- /experiments/view_transition/foo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Foo - View Transition Demo 7 | 8 | 9 | 10 | 11 | 17 | 18 |
19 |

Foo Page

20 |
21 |
22 |

This is the Foo page.

23 |

The card and title have view-transition-name, so they animate smoothly.

24 |
25 | 26 |
27 |

Foo Features

28 |
    29 |
  • Cross-document view transitions
  • 30 |
  • No JavaScript API call needed for MPA
  • 31 |
  • CSS @view-transition rule enables it
  • 32 |
33 |
34 | 35 | 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /e2e/coverage-fixture.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Playwright coverage fixture using V8 coverage API 3 | * Collects JS coverage during E2E tests 4 | */ 5 | import { test as base } from "@playwright/test"; 6 | import { mkdir, writeFile } from "node:fs/promises"; 7 | import { join, dirname } from "node:path"; 8 | import { fileURLToPath } from "node:url"; 9 | 10 | const __dirname = dirname(fileURLToPath(import.meta.url)); 11 | const COVERAGE_DIR = join(__dirname, "..", "coverage", "e2e-v8"); 12 | 13 | // Extend the base test with coverage collection 14 | export const test = base.extend({ 15 | coveragePage: async ({ page }, use, testInfo) => { 16 | // Start JS coverage before test 17 | await page.coverage.startJSCoverage({ 18 | resetOnNavigation: false, 19 | }); 20 | 21 | // Run the test 22 | await use(page); 23 | 24 | // Stop coverage and save 25 | const coverage = await page.coverage.stopJSCoverage(); 26 | 27 | // Filter to only include our app code (exclude node_modules, etc.) 28 | const filtered = coverage.filter((entry) => { 29 | const url = entry.url; 30 | return ( 31 | url.includes("/target/js/") || 32 | url.includes("/js/loader/") || 33 | url.includes("browser_router") || 34 | url.includes("demo") 35 | ); 36 | }); 37 | 38 | if (filtered.length > 0) { 39 | await mkdir(COVERAGE_DIR, { recursive: true }); 40 | 41 | // Save raw V8 coverage data 42 | const fileName = `${testInfo.testId.replace(/[^a-zA-Z0-9]/g, "_")}.json`; 43 | await writeFile( 44 | join(COVERAGE_DIR, fileName), 45 | JSON.stringify(filtered, null, 2) 46 | ); 47 | } 48 | }, 49 | }); 50 | 51 | export { expect } from "@playwright/test"; 52 | -------------------------------------------------------------------------------- /src/luna/routes/pkg.generated.mbti: -------------------------------------------------------------------------------- 1 | // Generated using `moon info`, DON'T EDIT IT 2 | package "mizchi/luna/luna/routes" 3 | 4 | // Values 5 | pub fn compile(Array[Routes], base? : String) -> Array[CompiledRoutes] 6 | 7 | pub fn match_url(String, Array[CompiledRoutes]) -> RoutesMatch? 8 | 9 | // Errors 10 | 11 | // Types and methods 12 | pub(all) struct CatchAllInfo { 13 | name : String 14 | optional : Bool 15 | } 16 | 17 | pub struct CompiledRoutes { 18 | pattern : String 19 | param_names : Array[String] 20 | component : String 21 | layouts : Array[String] 22 | kind : RoutesKind 23 | title : String 24 | meta : Array[(String, String)] 25 | catch_all : CatchAllInfo? 26 | } 27 | 28 | pub(all) enum Routes { 29 | Page(path~ : String, component~ : String, title~ : String, meta~ : Array[(String, String)]) 30 | Layout(segment~ : String, children~ : Array[Routes], layout~ : String) 31 | Get(path~ : String, handler~ : String) 32 | Post(path~ : String, handler~ : String) 33 | } 34 | 35 | pub enum RoutesKind { 36 | Page 37 | GetApi 38 | PostApi 39 | } 40 | pub impl Eq for RoutesKind 41 | pub impl Show for RoutesKind 42 | 43 | pub struct RoutesMatch { 44 | route : CompiledRoutes 45 | params : Array[(String, String)] 46 | query : Array[(String, String)] 47 | path : String 48 | } 49 | pub fn RoutesMatch::component(Self) -> String 50 | pub fn RoutesMatch::get_param(Self, String) -> String? 51 | pub fn RoutesMatch::get_query(Self, String) -> String? 52 | pub fn RoutesMatch::kind(Self) -> RoutesKind 53 | pub fn RoutesMatch::layouts(Self) -> Array[String] 54 | pub fn RoutesMatch::meta(Self) -> Array[(String, String)] 55 | pub fn RoutesMatch::title(Self) -> String 56 | 57 | // Type aliases 58 | 59 | // Traits 60 | 61 | -------------------------------------------------------------------------------- /e2e/playwright.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test"; 2 | import { dirname, join } from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | export default defineConfig({ 8 | testDir: __dirname, 9 | testMatch: ["**/*.test.ts", "**/*.test.mts"], 10 | testIgnore: [ 11 | "**/template-app/**", 12 | "**/sol/cli/**", 13 | "**/sol-app/**", 14 | "**/astra/**", // Uses its own config 15 | "**/wc_counter*.test.ts", 16 | "**/wc-css-isolation.test.ts", 17 | "**/wc-ssr-css.test.ts", 18 | // Visual snapshot tests are skipped in CI due to cross-platform rendering differences 19 | ...(process.env.CI ? ["**/visual-snapshots.test.ts"] : []), 20 | ], 21 | fullyParallel: true, 22 | forbidOnly: !!process.env.CI, 23 | retries: process.env.CI ? 2 : 0, 24 | workers: process.env.CI ? 1 : undefined, 25 | reporter: "dot", 26 | // Snapshot settings: remove platform suffix, allow small differences 27 | snapshotPathTemplate: "{testDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}", 28 | expect: { 29 | toHaveScreenshot: { 30 | maxDiffPixelRatio: 0.05, // Allow 5% pixel difference 31 | threshold: 0.3, // Per-pixel color threshold (0-1) 32 | }, 33 | }, 34 | use: { 35 | baseURL: "http://localhost:3456", 36 | trace: "on-first-retry", 37 | }, 38 | projects: [ 39 | { 40 | name: "chromium", 41 | use: { ...devices["Desktop Chrome"] }, 42 | }, 43 | ], 44 | webServer: { 45 | command: `node ${join(__dirname, "server.ts")}`, 46 | url: "http://127.0.0.1:3456", 47 | reuseExistingServer: !process.env.CI, 48 | stdout: "pipe", 49 | timeout: 30000, 50 | }, 51 | }); 52 | -------------------------------------------------------------------------------- /js/astra/assets/scripts/wc-loader.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function setupTrigger(el, t, cb) { 3 | if (t === 'load') document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', cb, {once:true}) : cb(); 4 | else if (t === 'idle') requestIdleCallback(cb); 5 | else if (t[0] === 'v') new IntersectionObserver((e,o) => { if(e.some(x=>x.isIntersecting)){o.disconnect();cb();}}, {rootMargin:'50px'}).observe(el); 6 | else if (t[0] === 'm') { var mq = matchMedia(t.slice(6)); var h = () => { if(mq.matches){mq.removeEventListener('change',h);cb();}}; mq.matches ? cb() : mq.addEventListener('change',h);} 7 | } 8 | var loaded = new Set(); 9 | async function hydrate(el) { 10 | var name = el.tagName.toLowerCase(); 11 | if (loaded.has(name)) return; 12 | var url = el.getAttribute('luna:wc-url'); 13 | if (!url) return; 14 | loaded.add(name); 15 | try { 16 | var mod = await import(url); 17 | var fn = mod.hydrate || mod.default; 18 | if (typeof fn === 'function') fn(el, {}, name); 19 | } catch(e) { console.error('[wc] Failed:', name, e); } 20 | } 21 | function setup(el) { setupTrigger(el, el.getAttribute('luna:wc-trigger') || 'load', () => hydrate(el)); } 22 | function scan() { document.querySelectorAll('[luna\\:wc-url]').forEach(setup); } 23 | if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', scan, {once:true}); 24 | else scan(); 25 | new MutationObserver(m => m.forEach(x => x.addedNodes.forEach(n => { if(n.nodeType === 1 && n.hasAttribute('luna:wc-url')) setup(n); }))).observe(document.body || document.documentElement, {childList:true,subtree:true}); 26 | window.__LUNA_WC_SCAN__ = scan; 27 | window.__LUNA_WC_CLEAR_LOADED__ = () => loaded.clear(); 28 | })(); 29 | -------------------------------------------------------------------------------- /src/astra/assets/scripts/wc-loader.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function setupTrigger(el, t, cb) { 3 | if (t === 'load') document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', cb, {once:true}) : cb(); 4 | else if (t === 'idle') requestIdleCallback(cb); 5 | else if (t[0] === 'v') new IntersectionObserver((e,o) => { if(e.some(x=>x.isIntersecting)){o.disconnect();cb();}}, {rootMargin:'50px'}).observe(el); 6 | else if (t[0] === 'm') { var mq = matchMedia(t.slice(6)); var h = () => { if(mq.matches){mq.removeEventListener('change',h);cb();}}; mq.matches ? cb() : mq.addEventListener('change',h);} 7 | } 8 | var loaded = new Set(); 9 | async function hydrate(el) { 10 | var name = el.tagName.toLowerCase(); 11 | if (loaded.has(name)) return; 12 | var url = el.getAttribute('luna:wc-url'); 13 | if (!url) return; 14 | loaded.add(name); 15 | try { 16 | var mod = await import(url); 17 | var fn = mod.hydrate || mod.default; 18 | if (typeof fn === 'function') fn(el, {}, name); 19 | } catch(e) { console.error('[wc] Failed:', name, e); } 20 | } 21 | function setup(el) { setupTrigger(el, el.getAttribute('luna:wc-trigger') || 'load', () => hydrate(el)); } 22 | function scan() { document.querySelectorAll('[luna\\:wc-url]').forEach(setup); } 23 | if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', scan, {once:true}); 24 | else scan(); 25 | new MutationObserver(m => m.forEach(x => x.addedNodes.forEach(n => { if(n.nodeType === 1 && n.hasAttribute('luna:wc-url')) setup(n); }))).observe(document.body || document.documentElement, {childList:true,subtree:true}); 26 | window.__LUNA_WC_SCAN__ = scan; 27 | window.__LUNA_WC_CLEAR_LOADED__ = () => loaded.clear(); 28 | })(); 29 | -------------------------------------------------------------------------------- /docs/components/my-counter.js: -------------------------------------------------------------------------------- 1 | // my-counter Web Component 2 | // A simple counter component that demonstrates Web Component hydration 3 | 4 | export function hydrate(element, _state, name) { 5 | let count = parseInt(element.getAttribute('initial') || '0', 10); 6 | 7 | const render = () => { 8 | element.innerHTML = ` 9 |
10 | ${name} 11 | 12 | ${count} 13 | 14 |
15 | `; 16 | 17 | const [decBtn, incBtn] = element.querySelectorAll("button"); 18 | decBtn.onclick = () => { count--; render(); }; 19 | incBtn.onclick = () => { count++; render(); }; 20 | }; 21 | 22 | render(); 23 | } 24 | 25 | export default hydrate; 26 | -------------------------------------------------------------------------------- /src/platform/server_dom/types.mbt: -------------------------------------------------------------------------------- 1 | // server_dom - Server-side DOM for SSR 2 | // 3 | // This module provides a DOM API for server-side rendering. 4 | // - No event handlers (E = Unit) 5 | // - Document structure elements (html, head, body) 6 | // - Island integration for client components 7 | // 8 | 9 | ///| 10 | /// Render result with HTML and preload URLs 11 | pub struct RenderResult { 12 | html : String 13 | preload_urls : Array[String] 14 | } 15 | 16 | // ============================================================================= 17 | // Server Node - Supports both sync and async server components 18 | // ============================================================================= 19 | 20 | ///| 21 | /// Server component result type 22 | /// Supports both synchronous and asynchronous server components: 23 | /// - Sync: returns Node[Unit] directly 24 | /// - Async: returns async function that resolves to Node[Unit] 25 | pub enum ServerNode { 26 | Sync(@luna.Node[Unit]) 27 | Async(async () -> @luna.Node[Unit]) 28 | } 29 | 30 | ///| 31 | /// Create a synchronous ServerNode from a Node 32 | pub fn ServerNode::sync(node : @luna.Node[Unit]) -> ServerNode { 33 | Sync(node) 34 | } 35 | 36 | ///| 37 | /// Create an asynchronous ServerNode from an async function 38 | pub fn ServerNode::async_(render : async () -> @luna.Node[Unit]) -> ServerNode { 39 | Async(render) 40 | } 41 | 42 | ///| 43 | /// Resolve ServerNode to Node (awaits if async) 44 | pub async fn ServerNode::resolve(self : ServerNode) -> @luna.Node[Unit] { 45 | match self { 46 | Sync(node) => node 47 | Async(render) => render() 48 | } 49 | } 50 | 51 | ///| 52 | /// Check if ServerNode is async 53 | pub fn ServerNode::is_async(self : ServerNode) -> Bool { 54 | match self { 55 | Async(_) => true 56 | Sync(_) => false 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/sol_app/static/loader.js: -------------------------------------------------------------------------------- 1 | import { createLoadedTracker, observeAdditions, onReady, setupTrigger } from "./lib.js"; 2 | 3 | //#region js/loader/src/loader.ts 4 | /*! luna loader v1 */ 5 | const d = document; 6 | const w = window; 7 | const S = {}; 8 | const { loaded, unload, clear } = createLoadedTracker(); 9 | const parseState = async (el) => { 10 | const a = el.getAttribute("luna:state"); 11 | if (!a) return; 12 | if (a[0] === "#") return JSON.parse(d.getElementById(a.slice(1))?.textContent ?? "null"); 13 | try { 14 | return JSON.parse(a); 15 | } catch {} 16 | }; 17 | const hydrate = async (el) => { 18 | const id = el.getAttribute("luna:id"); 19 | if (!id || loaded.has(id)) return; 20 | loaded.add(id); 21 | const url = el.getAttribute("luna:url"); 22 | if (!url) return; 23 | S[id] = await parseState(el); 24 | const mod = await import(url); 25 | const ex = el.getAttribute("luna:export"); 26 | (ex ? mod[ex] : mod.hydrate ?? mod.default)?.(el, S[id], id); 27 | }; 28 | const setup = (el) => { 29 | setupTrigger(el, el.getAttribute("luna:client-trigger") ?? "load", () => hydrate(el)); 30 | }; 31 | const scan = () => { 32 | d.querySelectorAll("[luna\\:id]").forEach(setup); 33 | }; 34 | d.querySelectorAll("script[type=\"luna/json\"]").forEach((s) => { 35 | if (s.id) S[s.id] = JSON.parse(s.textContent ?? "{}"); 36 | }); 37 | onReady(scan); 38 | observeAdditions((el) => el.hasAttribute("luna:id"), setup); 39 | const unloadAll = (root = d.body) => { 40 | root.querySelectorAll("[luna\\:id]").forEach((el) => { 41 | const id = el.getAttribute("luna:id"); 42 | if (id) loaded.delete(id); 43 | }); 44 | }; 45 | w.__LUNA_STATE__ = S; 46 | w.__LUNA_HYDRATE__ = hydrate; 47 | w.__LUNA_SCAN__ = scan; 48 | w.__LUNA_UNLOAD__ = unload; 49 | w.__LUNA_UNLOAD_ALL__ = unloadAll; 50 | w.__LUNA_CLEAR_LOADED__ = clear; 51 | 52 | //#endregion -------------------------------------------------------------------------------- /docs/00_introduction/03_faq/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FAQ 3 | --- 4 | 5 | # Frequently Asked Questions 6 | 7 | ## General 8 | 9 | ### What is Luna? 10 | 11 | Luna is a suite of tools for building modern web applications with MoonBit and JavaScript. It provides: 12 | 13 | - **Luna Core** - Signals, Islands, and fine-grained reactivity 14 | - **Astra** - Static Site Generator for documentation 15 | - **Sol** - Full-stack SSR framework 16 | - **Stella** - Development tools 17 | 18 | ### Why MoonBit? 19 | 20 | MoonBit offers: 21 | - Compile-time type safety (no runtime errors) 22 | - Native speed for SSR (no V8 overhead) 23 | - Guaranteed dead code elimination 24 | - Small, optimized output 25 | 26 | ### Is Luna production-ready? 27 | 28 | Luna is currently experimental. APIs may change. Use in production at your own discretion. 29 | 30 | ## Technical 31 | 32 | ### How does Luna compare to React? 33 | 34 | | Aspect | React | Luna | 35 | |--------|-------|------| 36 | | Update Model | Virtual DOM diffing | Fine-grained signals | 37 | | Hydration | Full page | Partial (islands) | 38 | | Runtime Size | ~42KB | ~3KB | 39 | | Language | JavaScript | MoonBit + JavaScript | 40 | 41 | ### What is Islands Architecture? 42 | 43 | Islands Architecture is a pattern where most of the page is static HTML, with only interactive "islands" loading JavaScript. This results in: 44 | 45 | - Faster initial page load 46 | - Smaller JavaScript bundles 47 | - Better performance on low-end devices 48 | 49 | ### Can I use Luna with existing frameworks? 50 | 51 | Luna is designed as a standalone solution. However, you can embed Luna islands in any HTML page. 52 | 53 | ## Development 54 | 55 | ### How do I report bugs? 56 | 57 | Open an issue on the GitHub repository. 58 | 59 | ### How do I contribute? 60 | 61 | See the contributing guide in the repository. 62 | -------------------------------------------------------------------------------- /scripts/lib/cov-reporter/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Coverage data types 3 | */ 4 | 5 | export interface LineCoverage { 6 | line: number; 7 | count: number; 8 | } 9 | 10 | export interface FileCoverage { 11 | path: string; 12 | lines: LineCoverage[]; 13 | source?: string; 14 | } 15 | 16 | export interface CoverageStats { 17 | files: number; 18 | totalLines: number; 19 | coveredLines: number; 20 | rate: number; 21 | } 22 | 23 | export interface DirectoryStats { 24 | covered: number; 25 | total: number; 26 | } 27 | 28 | export interface CoverageConfig { 29 | projectRoot: string; 30 | coverageDir: string; 31 | sourceMapDir: string; 32 | include?: RegExp; 33 | exclude?: RegExp; 34 | /** Additional directories to exclude from coverage reporting */ 35 | excludeDirs?: string[]; 36 | } 37 | 38 | /** 39 | * Check if a file path should be excluded based on config 40 | */ 41 | export function shouldExcludeFile( 42 | filepath: string, 43 | config: CoverageConfig 44 | ): boolean { 45 | // Check regex exclude pattern 46 | if (config.exclude && config.exclude.test(filepath)) { 47 | return true; 48 | } 49 | // Check directory exclusions 50 | if (config.excludeDirs) { 51 | for (const dir of config.excludeDirs) { 52 | if (filepath.startsWith(dir + "/") || filepath === dir) { 53 | return true; 54 | } 55 | } 56 | } 57 | return false; 58 | } 59 | 60 | export function createDefaultConfig(projectRoot: string): CoverageConfig { 61 | return { 62 | projectRoot, 63 | coverageDir: `${projectRoot}/coverage`, 64 | sourceMapDir: `${projectRoot}/target/js/debug/build`, 65 | include: /^src\//, 66 | exclude: /_test\.mbt$/, 67 | // Exclude benchmarks, examples, and test infrastructure 68 | excludeDirs: ["src/_bench", "src/examples", "src/tests"], 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /demo-src/game/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Luna Benchmark Game 7 | 58 | 59 | 60 |
61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /demo-src/wc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Luna WC Examples (Web Components) 7 | 21 | 22 | 23 | 24 |
25 |

Luna WC Examples

26 |

A collection of examples demonstrating Web Components with MoonBit.

27 |
28 | 29 | 30 | 31 |
32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 | 50 | 51 |
52 | 53 | 54 |

Nested Components

55 |

Child components dispatch custom events that bubble through Shadow DOM to parent.

56 | 57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /src/examples/game/index.html: -------------------------------------------------------------------------------- 1 | 64 | --------------------------------------------------------------------------------