fuzor 0.0.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +97 -0
- package/dist/cli.js.map +1 -0
- package/dist/compiler/index.d.ts +28 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/index.js +184 -0
- package/dist/compiler/index.js.map +1 -0
- package/dist/compiler/islands.d.ts +17 -0
- package/dist/compiler/islands.d.ts.map +1 -0
- package/dist/compiler/islands.js +78 -0
- package/dist/compiler/islands.js.map +1 -0
- package/dist/create.d.ts +21 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +111 -0
- package/dist/create.js.map +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/islands/react.d.ts +8 -0
- package/dist/islands/react.d.ts.map +1 -0
- package/dist/islands/react.js +19 -0
- package/dist/islands/react.js.map +1 -0
- package/dist/islands/solid.d.ts +8 -0
- package/dist/islands/solid.d.ts.map +1 -0
- package/dist/islands/solid.js +15 -0
- package/dist/islands/solid.js.map +1 -0
- package/dist/islands/vue.d.ts +5 -0
- package/dist/islands/vue.d.ts.map +1 -0
- package/dist/islands/vue.js +13 -0
- package/dist/islands/vue.js.map +1 -0
- package/dist/runtime/atom.d.ts +10 -0
- package/dist/runtime/atom.d.ts.map +1 -0
- package/dist/runtime/atom.js +38 -0
- package/dist/runtime/atom.js.map +1 -0
- package/dist/runtime/index.d.ts +31 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +121 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/islands.d.ts +6 -0
- package/dist/runtime/islands.d.ts.map +1 -0
- package/dist/runtime/islands.js +43 -0
- package/dist/runtime/islands.js.map +1 -0
- package/dist/ssr/node.d.ts +7 -0
- package/dist/ssr/node.d.ts.map +1 -0
- package/dist/ssr/node.js +46 -0
- package/dist/ssr/node.js.map +1 -0
- package/dist/ssr.d.ts +10 -0
- package/dist/ssr.d.ts.map +1 -0
- package/dist/ssr.js +19 -0
- package/dist/ssr.js.map +1 -0
- package/dist/vite.d.ts +8 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +310 -0
- package/dist/vite.js.map +1 -0
- package/package.json +71 -56
- package/templates/basic/README.md +16 -0
- package/templates/basic/_gitignore +3 -0
- package/templates/basic/components/app-badge.html +1 -0
- package/templates/basic/index.html +12 -0
- package/templates/basic/islands/counter.ts +13 -0
- package/templates/basic/package.json +20 -0
- package/templates/basic/pages/about/index.html +5 -0
- package/templates/basic/pages/docs/index.md +9 -0
- package/templates/basic/pages/index.html +9 -0
- package/templates/basic/pages/layout.html +8 -0
- package/templates/basic/pnpm-workspace.yaml +5 -0
- package/templates/basic/server.mjs +10 -0
- package/templates/basic/style.css +1 -0
- package/templates/basic/tsconfig.json +13 -0
- package/templates/basic/vite.config.ts +4 -0
- package/bin/index.js +0 -13
- package/dist/core/init.d.ts +0 -7
- package/dist/core/router.d.ts +0 -4
- package/dist/core/vite-config.d.ts +0 -5
- package/dist/init.cjs +0 -237
- package/dist/init.js +0 -468
- package/dist/lib.cjs +0 -222
- package/dist/lib.js +0 -19247
- package/dist/render/lit.d.ts +0 -1
- package/dist/render/preact.d.ts +0 -1
- package/dist/render/react.d.ts +0 -7
- package/dist/render/solid.d.ts +0 -1
- package/dist/render/svelte.d.ts +0 -6
- package/dist/render/vue.d.ts +0 -1
- package/dist/utils/create-file-if-not-exists.d.ts +0 -1
- package/dist/utils/create-folder-if-not-exist.d.ts +0 -2
- package/dist/utils/default-theme-files.d.ts +0 -7
- package/dist/utils/file-path-helpers.d.ts +0 -3
- package/dist/utils/is-valid-wordpress-slug.d.ts +0 -17
- package/dist/utils/read-write-file.d.ts +0 -1
- package/dist/utils/remove-file-exists.d.ts +0 -2
- package/dist/utils/remove-trailing-slash-url.d.ts +0 -1
- package/dist/utils/router-helpers.d.ts +0 -19
- package/dist/utils/zip-wp-theme.d.ts +0 -2
- package/types/index.d.ts +0 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-present Ivan Jeremic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Fuzor
|
|
2
|
+
|
|
3
|
+
**One source app. Three ways to ship.** Write your routes, layouts, components, and islands once, then compile that same source tree as a SPA, a static MPA, or a server-served app. Choose a build target, and Fuzor's compiler produces the right output for it. Your pages and islands keep the same appearance and behavior in every mode, with no rewrites or separate implementations.
|
|
4
|
+
|
|
5
|
+
Fuzor is an early HTML-first framework prototype. Pages can be HTML or Markdown, with interactive islands where needed. The Fuzor CLI selects the build target.
|
|
6
|
+
|
|
7
|
+
| Target | What the build produces | How navigation works |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `spa` | One HTML document containing the compiled route templates, plus an ESM entry and lazy island chunks | The browser selects templates with the History API; route content is already in the initial document |
|
|
10
|
+
| `mpa` | A standalone HTML file per route and client assets | Ordinary links load the next document; suitable for static hosting |
|
|
11
|
+
| `ssr` | A server entry containing complete, preassembled route documents and client assets | The server looks up the requested route and sends its document |
|
|
12
|
+
|
|
13
|
+
Despite the `ssr` name, the current server target **prerenders at build time**. It does not render or stitch page HTML on each request. The MPA target is also static generation; there is no separate SSG mode. Add `--inline` to a SPA build to put its JavaScript and CSS into a single HTML file for embedded or offline UIs.
|
|
14
|
+
|
|
15
|
+
## Try it locally
|
|
16
|
+
|
|
17
|
+
This repository contains a [mixed-framework example](./examples/basic) and the [Fuzor website](./website), which is itself a Fuzor app. From the repository root:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm install
|
|
21
|
+
pnpm build
|
|
22
|
+
pnpm example:dev:ssr
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Open the address printed by Vite. On the example homepage, **View Page Source** shows the vanilla, React, Solid, and Vue counter markup before the browser activates the islands. Click the counters to see the client behavior attach to that markup. The same example can run in the other development targets:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm --filter @fuzor/example dev:spa
|
|
29
|
+
pnpm --filter @fuzor/example dev:mpa
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Build each output from those same source files:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm example:spa # examples/basic/dist-spa/
|
|
36
|
+
pnpm example:spa --inline # single dist-spa/index.html
|
|
37
|
+
pnpm example:mpa # examples/basic/dist-mpa/
|
|
38
|
+
pnpm example:ssr # examples/basic/dist-ssr/
|
|
39
|
+
pnpm --filter @fuzor/example start:ssr # serve the built SSR target on port 4173
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Run `pnpm website:dev` for the website, or `pnpm website:build` for its static MPA output. Its homepage is HTML; the `/docs` route tree is Markdown files and folders under [`website/pages/docs/`](./website/pages/docs), with a shared docs layout in [`website/layouts/docs.html`](./website/layouts/docs.html).
|
|
43
|
+
|
|
44
|
+
## How an app is written
|
|
45
|
+
|
|
46
|
+
Routes are folders containing `index.html` or `index.md`. A route can inherit layouts and use reusable HTML components. Interactive regions are explicit islands:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
my-app/
|
|
50
|
+
├── index.html # document shell
|
|
51
|
+
├── pages/
|
|
52
|
+
│ ├── index.html # /
|
|
53
|
+
│ ├── about/index.html # /about/
|
|
54
|
+
│ └── docs/index.md # /docs/
|
|
55
|
+
├── components/
|
|
56
|
+
│ └── app-header.html # <app-header>
|
|
57
|
+
└── islands/
|
|
58
|
+
└── counter.ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<fuzor-link href="/about/">About</fuzor-link>
|
|
63
|
+
<fuzor-island src="counter" props='{"initial":1}' client:load></fuzor-island>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`<fuzor-link>` becomes a real anchor, so links work without JavaScript in the MPA and server targets. In the SPA, the router handles navigation and replaces route markup. A vanilla island exports `mount(host, props)` and may return a cleanup function. It can also export `render(props)` to provide initial HTML at build time. `client:load`, `client:idle`, and `client:visible` control when an island activates.
|
|
67
|
+
|
|
68
|
+
React, Solid, and Vue islands in the example render their initial HTML **during compilation**, then hydrate inside separate DOM hosts in the browser. Their frameworks do not own each other's DOM. React and Solid JSX use file-specific Vite plugin filters (`*.react.tsx` and `*.solid.tsx`) so their transforms do not collide; Solid's plugin enables SSR rendering. This is renderer-based build-time output, not static extraction of JSX templates. See the [island architecture and its constraints](./design/islands.md).
|
|
69
|
+
|
|
70
|
+
For contributors: the compiler uses [Effect v4](https://effect.website/) services and Layers for file access, typed compilation errors, and bounded concurrent directory work. It validates route and layout structure and renders Markdown to HTML at build time with [TanStack Markdown](https://tanstack.com/markdown). The runtime provides browser and in-memory history hosts plus schema-checked URL atoms.
|
|
71
|
+
|
|
72
|
+
## CLI and new apps
|
|
73
|
+
|
|
74
|
+
The CLI uses Citty for commands and Clack for interactive creation and progress. It selects a target while Vite handles serving and bundling:
|
|
75
|
+
|
|
76
|
+
Create an app with the official scaffold command:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm create fuzor@latest my-app
|
|
80
|
+
cd my-app
|
|
81
|
+
pnpm install
|
|
82
|
+
pnpm dev
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The creator can install dependencies for you; if you accept that prompt, the separate `pnpm install` is optional. It generates one source tree with scripts for all three targets. The `create-fuzor` npm package powers this command.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
fuzor dev --target spa # also mpa or ssr; accepts --host and --port
|
|
89
|
+
fuzor build --target spa
|
|
90
|
+
fuzor build --target spa --inline
|
|
91
|
+
fuzor build --target mpa
|
|
92
|
+
fuzor build --target ssr
|
|
93
|
+
fuzor create my-app
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A Vite config can simply add `fuzorPlugin()`. The [starter template](./templates/basic) includes scripts for all three targets, a Markdown route, and a vanilla island. The `fuzor create my-app` command calls the same creator if Fuzor is already installed. Deployment adapters and `fuzor deploy` are not implemented.
|
|
97
|
+
|
|
98
|
+
## Current boundaries
|
|
99
|
+
|
|
100
|
+
Fuzor currently compiles **known, static folder routes**. Dynamic URL parameters, request-time page data, forms, per-page revalidation, and request-time rendering are future work. Build-time island previews require known JSON props and a compatible renderer; other islands begin with an empty host. Arbitrary asynchronous content cannot yet promise a layout-shift-free placeholder. Multiple versions of one UI framework in a single build are not guaranteed; additional JSX systems need their own tested adapters.
|
|
101
|
+
|
|
102
|
+
The SPA carries every route's HTML in its initial document, so that document grows with the route tree, even though its island code loads lazily. SPA deep links need a hosting fallback to `index.html`. The inline build requires assets it can embed.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pnpm test
|
|
106
|
+
pnpm typecheck
|
|
107
|
+
pnpm --filter @fuzor/example typecheck
|
|
108
|
+
pnpm verify:profiles
|
|
109
|
+
pnpm website:verify
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Further detail lives in the [website docs](./website/pages/docs) and the [example walkthrough](./examples/basic/README.md).
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { defineCommand, runMain } from "citty";
|
|
4
|
+
import * as prompts from "@clack/prompts";
|
|
5
|
+
import colors from "picocolors";
|
|
6
|
+
import { createCommand } from "./create.js";
|
|
7
|
+
const packageJson = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
|
|
8
|
+
const targetArg = () => ({ type: "enum", options: ["spa", "mpa", "ssr"], default: "spa", description: "Output profile" });
|
|
9
|
+
function selectTarget(target, inline = false) {
|
|
10
|
+
process.env.FUZOR_TARGET = target;
|
|
11
|
+
process.env.FUZOR_INLINE = inline ? "1" : "0";
|
|
12
|
+
}
|
|
13
|
+
const dev = defineCommand({
|
|
14
|
+
meta: { name: "dev", description: "Start Vite with a Fuzor profile" },
|
|
15
|
+
args: {
|
|
16
|
+
target: targetArg(),
|
|
17
|
+
host: { type: "string", description: "Host to listen on" },
|
|
18
|
+
port: { type: "string", description: "Port to listen on" }
|
|
19
|
+
},
|
|
20
|
+
async run({ args }) {
|
|
21
|
+
const target = (args.target ?? "spa");
|
|
22
|
+
selectTarget(target);
|
|
23
|
+
const port = args.port === undefined ? undefined : Number(args.port);
|
|
24
|
+
if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535))
|
|
25
|
+
throw new Error("--port must be between 1 and 65535.");
|
|
26
|
+
prompts.intro(colors.cyan(colors.bold(`◉ Fuzor ${target.toUpperCase()}`)));
|
|
27
|
+
const spin = process.stdout.isTTY ? prompts.spinner() : undefined;
|
|
28
|
+
if (spin)
|
|
29
|
+
spin.start("Starting development server");
|
|
30
|
+
else
|
|
31
|
+
console.log("Starting development server...");
|
|
32
|
+
try {
|
|
33
|
+
const { createServer } = await import("vite");
|
|
34
|
+
const server = await createServer({
|
|
35
|
+
server: { ...(args.host === undefined ? {} : { host: args.host }), ...(port === undefined ? {} : { port }) },
|
|
36
|
+
logLevel: "error"
|
|
37
|
+
});
|
|
38
|
+
await server.listen();
|
|
39
|
+
if (spin)
|
|
40
|
+
spin.stop("Development server ready");
|
|
41
|
+
else
|
|
42
|
+
console.log("Development server ready");
|
|
43
|
+
for (const url of server.resolvedUrls?.local ?? [])
|
|
44
|
+
console.log(` ${colors.green("Local")} ${url}`);
|
|
45
|
+
for (const url of server.resolvedUrls?.network ?? [])
|
|
46
|
+
console.log(` ${colors.green("Network")} ${url}`);
|
|
47
|
+
const close = () => { void server.close().then(() => { process.exitCode = 0; }); };
|
|
48
|
+
process.once("SIGINT", close);
|
|
49
|
+
process.once("SIGTERM", close);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (spin)
|
|
53
|
+
spin.stop("Could not start development server");
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const buildCommand = defineCommand({
|
|
59
|
+
meta: { name: "build", description: "Build a Fuzor profile with Vite" },
|
|
60
|
+
args: {
|
|
61
|
+
target: targetArg(),
|
|
62
|
+
inline: { type: "boolean", description: "Inline SPA JavaScript and CSS into one HTML file" }
|
|
63
|
+
},
|
|
64
|
+
async run({ args }) {
|
|
65
|
+
const target = (args.target ?? "spa");
|
|
66
|
+
if (args.inline && target !== "spa")
|
|
67
|
+
throw new Error("--inline is only supported for the SPA target.");
|
|
68
|
+
selectTarget(target, args.inline);
|
|
69
|
+
const outDir = target === "ssr" ? "dist-ssr/client" : `dist-${target}`;
|
|
70
|
+
prompts.intro(colors.cyan(colors.bold(`◉ Fuzor ${target.toUpperCase()}`)));
|
|
71
|
+
const spin = process.stdout.isTTY ? prompts.spinner() : undefined;
|
|
72
|
+
if (spin)
|
|
73
|
+
spin.start(args.inline ? "Building inline SPA" : `Building ${target.toUpperCase()}`);
|
|
74
|
+
else
|
|
75
|
+
console.log(args.inline ? "Building inline SPA..." : `Building ${target.toUpperCase()}...`);
|
|
76
|
+
try {
|
|
77
|
+
const { build } = await import("vite");
|
|
78
|
+
await build({ build: { outDir }, logLevel: "error" });
|
|
79
|
+
if (spin)
|
|
80
|
+
spin.stop(`Built ${colors.green(outDir)}`);
|
|
81
|
+
else
|
|
82
|
+
console.log(`Built ${outDir}`);
|
|
83
|
+
prompts.outro(args.inline ? "One HTML file, ready to ship" : "Build complete");
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (spin)
|
|
87
|
+
spin.stop("Build failed");
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const main = defineCommand({
|
|
93
|
+
meta: { name: "fuzor", version: packageJson.version, description: "Compile one app for every Fuzor target" },
|
|
94
|
+
subCommands: { dev, build: buildCommand, create: createCommand }
|
|
95
|
+
});
|
|
96
|
+
void runMain(main);
|
|
97
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,MAAM,MAAM,YAAY,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAwB,CAAC;AAC3H,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAEnI,SAAS,YAAY,CAAC,MAAc,EAAE,MAAM,GAAG,KAAK;IAClD,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,MAAM,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AAChD,CAAC;AAED,MAAM,GAAG,GAAG,aAAa,CAAC;IACxB,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,iCAAiC,EAAE;IACrE,IAAI,EAAE;QACJ,MAAM,EAAE,SAAS,EAAE;QACnB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE;QAC1D,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE;KAC3D;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAW,CAAC;QAChD,YAAY,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACxI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,IAAI,IAAI;YAAE,IAAI,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;;YAC/C,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;gBAChC,MAAM,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;gBAC5G,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;YACH,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;;gBAC3C,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;YAC7C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;YACtG,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,YAAY,EAAE,OAAO,IAAI,EAAE;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;YAC1G,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAC1D,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,aAAa,CAAC;IACjC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,iCAAiC,EAAE;IACvE,IAAI,EAAE;QACJ,MAAM,EAAE,SAAS,EAAE;QACnB,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,kDAAkD,EAAE;KAC7F;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;QAChB,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAW,CAAC;QAChD,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACvG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,IAAI,IAAI;YAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;;YAC1F,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QACjG,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YACtD,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;;gBAChD,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;YACpC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;QACjF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACpC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,aAAa,CAAC;IACzB,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,wCAAwC,EAAE;IAC5G,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE;CACjE,CAAC,CAAC;AAEH,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Dirent, Stats } from "node:fs";
|
|
2
|
+
import { Context, Effect, Layer } from "effect";
|
|
3
|
+
export type CompiledRoute = {
|
|
4
|
+
readonly path: string;
|
|
5
|
+
readonly source: string;
|
|
6
|
+
readonly html: string;
|
|
7
|
+
};
|
|
8
|
+
export type CompiledProject = {
|
|
9
|
+
readonly routes: readonly CompiledRoute[];
|
|
10
|
+
readonly files: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
export declare class CompilerError extends Error {
|
|
13
|
+
readonly _tag = "CompilerError";
|
|
14
|
+
}
|
|
15
|
+
export type CompilerFileSystemShape = {
|
|
16
|
+
readonly read: (file: string) => Effect.Effect<string, CompilerError>;
|
|
17
|
+
readonly entries: (directory: string) => Effect.Effect<Dirent[], CompilerError>;
|
|
18
|
+
readonly stat: (file: string) => Effect.Effect<Stats, CompilerError>;
|
|
19
|
+
readonly exists: (file: string) => Effect.Effect<boolean, CompilerError>;
|
|
20
|
+
};
|
|
21
|
+
export declare const CompilerFileSystem: Context.Service<CompilerFileSystemShape, CompilerFileSystemShape>;
|
|
22
|
+
export declare const CompilerFileSystemLive: Layer.Layer<CompilerFileSystemShape, never, never>;
|
|
23
|
+
export declare function compileProjectEffect(root: string): Effect.Effect<{
|
|
24
|
+
routes: CompiledRoute[];
|
|
25
|
+
files: string[];
|
|
26
|
+
}, CompilerError, CompilerFileSystemShape>;
|
|
27
|
+
export declare function compileProject(root: string): Promise<CompiledProject>;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compiler/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAKhD,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC,CAAC;AAIF,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,mBAAmB;CACjC;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACtE,QAAQ,CAAC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC;IAChF,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACrE,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;CAC1E,CAAC;AAEF,eAAO,MAAM,kBAAkB,mEAAsE,CAAC;AAKtG,eAAO,MAAM,sBAAsB,oDAWC,CAAC;AAuErC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM;;;2CAiFhD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAErE"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Context, Effect, Layer } from "effect";
|
|
4
|
+
import { parseMarkdown } from "@tanstack/markdown/parser";
|
|
5
|
+
import { renderHtml } from "@tanstack/markdown/html";
|
|
6
|
+
import { parseFragment } from "parse5";
|
|
7
|
+
const slotPattern = /<slot\s*(?:\/\s*>|>\s*<\/slot\s*>)/i;
|
|
8
|
+
export class CompilerError extends Error {
|
|
9
|
+
_tag = "CompilerError";
|
|
10
|
+
}
|
|
11
|
+
export const CompilerFileSystem = Context.Service("FuzorCompilerFileSystem");
|
|
12
|
+
const io = (label, action) => Effect.tryPromise({ try: action, catch: (error) => new CompilerError(`${label}: ${String(error)}`) });
|
|
13
|
+
export const CompilerFileSystemLive = Layer.succeed(CompilerFileSystem, {
|
|
14
|
+
read: (file) => io(`Cannot read ${file}`, () => readFile(file, "utf8")),
|
|
15
|
+
entries: (directory) => io(`Cannot list ${directory}`, () => readdir(directory, { withFileTypes: true })),
|
|
16
|
+
stat: (file) => io(`Cannot stat ${file}`, () => stat(file)),
|
|
17
|
+
exists: (file) => io(`Cannot check ${file}`, async () => {
|
|
18
|
+
try {
|
|
19
|
+
return (await stat(file)).isFile();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.code === "ENOENT")
|
|
23
|
+
return false;
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
function routePath(relativeDir) {
|
|
29
|
+
return relativeDir === "" ? "/" : `/${relativeDir.split(path.sep).join("/")}/`;
|
|
30
|
+
}
|
|
31
|
+
function fillSlot(layout, content, label) {
|
|
32
|
+
if (!slotPattern.test(layout)) {
|
|
33
|
+
throw new Error(`${label} must contain a default <slot></slot>.`);
|
|
34
|
+
}
|
|
35
|
+
return layout.replace(slotPattern, () => content);
|
|
36
|
+
}
|
|
37
|
+
function expandComponents(html, components, stack = []) {
|
|
38
|
+
const fragment = parseFragment(html, { sourceCodeLocationInfo: true });
|
|
39
|
+
const replacements = [];
|
|
40
|
+
const visit = (node) => {
|
|
41
|
+
if (!("tagName" in node))
|
|
42
|
+
return;
|
|
43
|
+
const name = node.tagName;
|
|
44
|
+
const source = components.get(name);
|
|
45
|
+
if (source) {
|
|
46
|
+
if (stack.includes(name))
|
|
47
|
+
throw new Error(`Component cycle: ${[...stack, name].join(" -> ")}`);
|
|
48
|
+
const location = node.sourceCodeLocation;
|
|
49
|
+
if (!location?.startTag || !location.endTag)
|
|
50
|
+
throw new Error(`Component <${name}> must have a closing tag.`);
|
|
51
|
+
const children = html.slice(location.startTag.endOffset, location.endTag.startOffset);
|
|
52
|
+
const nested = expandComponents(children, components, stack);
|
|
53
|
+
const body = slotPattern.test(source) ? fillSlot(source, nested, `Component ${name}`) : source;
|
|
54
|
+
replacements.push({
|
|
55
|
+
start: location.startOffset,
|
|
56
|
+
end: location.endOffset,
|
|
57
|
+
value: expandComponents(body, components, [...stack, name])
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
for (const child of node.childNodes)
|
|
62
|
+
visit(child);
|
|
63
|
+
if (node.tagName === "template") {
|
|
64
|
+
for (const child of node.content.childNodes)
|
|
65
|
+
visit(child);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
for (const node of fragment.childNodes)
|
|
69
|
+
visit(node);
|
|
70
|
+
for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
|
|
71
|
+
html = html.slice(0, replacement.start) + replacement.value + html.slice(replacement.end);
|
|
72
|
+
}
|
|
73
|
+
return html;
|
|
74
|
+
}
|
|
75
|
+
function compileLinks(html) {
|
|
76
|
+
const fragment = parseFragment(html, { sourceCodeLocationInfo: true });
|
|
77
|
+
const replacements = [];
|
|
78
|
+
const visit = (node) => {
|
|
79
|
+
if (!("tagName" in node))
|
|
80
|
+
return;
|
|
81
|
+
if (node.tagName === "fuzor-link") {
|
|
82
|
+
const location = node.sourceCodeLocation;
|
|
83
|
+
if (!location?.startTag || !location.endTag)
|
|
84
|
+
throw new Error("<fuzor-link> must have a closing tag.");
|
|
85
|
+
const opening = html.slice(location.startTag.startOffset, location.startTag.endOffset)
|
|
86
|
+
.replace(/^<fuzor-link\b/i, "<a data-fuzor-link");
|
|
87
|
+
replacements.push({ start: location.startTag.startOffset, end: location.startTag.endOffset, value: opening });
|
|
88
|
+
replacements.push({ start: location.endTag.startOffset, end: location.endTag.endOffset, value: "</a>" });
|
|
89
|
+
}
|
|
90
|
+
for (const child of node.childNodes)
|
|
91
|
+
visit(child);
|
|
92
|
+
if (node.tagName === "template") {
|
|
93
|
+
for (const child of node.content.childNodes)
|
|
94
|
+
visit(child);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
for (const node of fragment.childNodes)
|
|
98
|
+
visit(node);
|
|
99
|
+
for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
|
|
100
|
+
html = html.slice(0, replacement.start) + replacement.value + html.slice(replacement.end);
|
|
101
|
+
}
|
|
102
|
+
return html;
|
|
103
|
+
}
|
|
104
|
+
export function compileProjectEffect(root) {
|
|
105
|
+
return Effect.gen(function* () {
|
|
106
|
+
const fs = yield* CompilerFileSystem;
|
|
107
|
+
const projectRoot = path.resolve(root);
|
|
108
|
+
const pagesRoot = path.join(projectRoot, "pages");
|
|
109
|
+
const componentsRoot = path.join(projectRoot, "components");
|
|
110
|
+
const layoutsRoot = path.join(projectRoot, "layouts");
|
|
111
|
+
for (const directory of [pagesRoot, componentsRoot]) {
|
|
112
|
+
const directoryStat = yield* fs.stat(directory);
|
|
113
|
+
if (!directoryStat.isDirectory())
|
|
114
|
+
yield* Effect.fail(new CompilerError(`${directory} must be a directory.`));
|
|
115
|
+
}
|
|
116
|
+
const files = [];
|
|
117
|
+
const components = new Map();
|
|
118
|
+
const componentEntries = yield* fs.entries(componentsRoot);
|
|
119
|
+
yield* Effect.forEach(componentEntries, (entry) => Effect.gen(function* () {
|
|
120
|
+
if (!entry.isFile() || !entry.name.endsWith(".html"))
|
|
121
|
+
return;
|
|
122
|
+
const name = entry.name.slice(0, -5);
|
|
123
|
+
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$/.test(name) || name.startsWith("fuzor-")) {
|
|
124
|
+
yield* Effect.fail(new CompilerError(`Invalid component filename: ${entry.name}. Use kebab-case and reserve fuzor-* for the framework.`));
|
|
125
|
+
}
|
|
126
|
+
const file = path.join(componentsRoot, entry.name);
|
|
127
|
+
components.set(name, yield* fs.read(file));
|
|
128
|
+
files.push(file);
|
|
129
|
+
}), { concurrency: 8, discard: true });
|
|
130
|
+
const routes = [];
|
|
131
|
+
const visit = (directory, relativeDir, layouts) => Effect.gen(function* () {
|
|
132
|
+
const entries = yield* fs.entries(directory);
|
|
133
|
+
const layoutFile = path.join(directory, "layout.html");
|
|
134
|
+
const externalLayoutFile = relativeDir ? path.join(layoutsRoot, `${relativeDir}.html`) : "";
|
|
135
|
+
const hasLocalLayout = yield* fs.exists(layoutFile);
|
|
136
|
+
const hasExternalLayout = externalLayoutFile ? yield* fs.exists(externalLayoutFile) : false;
|
|
137
|
+
if (hasLocalLayout && hasExternalLayout) {
|
|
138
|
+
yield* Effect.fail(new CompilerError(`Duplicate layout for /${relativeDir}/: ${layoutFile} and ${externalLayoutFile}`));
|
|
139
|
+
}
|
|
140
|
+
const selectedLayout = hasLocalLayout ? layoutFile : hasExternalLayout ? externalLayoutFile : undefined;
|
|
141
|
+
const ownLayouts = selectedLayout
|
|
142
|
+
? [...layouts, yield* fs.read(selectedLayout)]
|
|
143
|
+
: [...layouts];
|
|
144
|
+
if (selectedLayout)
|
|
145
|
+
files.push(selectedLayout);
|
|
146
|
+
const htmlFile = path.join(directory, "index.html");
|
|
147
|
+
const mdFile = path.join(directory, "index.md");
|
|
148
|
+
const hasHtml = yield* fs.exists(htmlFile);
|
|
149
|
+
const hasMarkdown = yield* fs.exists(mdFile);
|
|
150
|
+
if (hasHtml && hasMarkdown)
|
|
151
|
+
yield* Effect.fail(new CompilerError(`${directory} has both index.html and index.md.`));
|
|
152
|
+
if (hasHtml || hasMarkdown) {
|
|
153
|
+
const source = hasHtml ? htmlFile : mdFile;
|
|
154
|
+
const raw = yield* fs.read(source);
|
|
155
|
+
files.push(source);
|
|
156
|
+
let content = hasMarkdown ? yield* Effect.try({
|
|
157
|
+
try: () => renderHtml(parseMarkdown(raw)),
|
|
158
|
+
catch: (error) => new CompilerError(`Markdown error in ${source}: ${String(error)}`)
|
|
159
|
+
}) : raw;
|
|
160
|
+
content = yield* Effect.try({
|
|
161
|
+
try: () => {
|
|
162
|
+
for (let index = ownLayouts.length - 1; index >= 0; index--) {
|
|
163
|
+
content = fillSlot(ownLayouts[index], content, `Layout ${index + 1} for ${source}`);
|
|
164
|
+
}
|
|
165
|
+
return compileLinks(expandComponents(content, components));
|
|
166
|
+
},
|
|
167
|
+
catch: (error) => new CompilerError(`Compilation failed for ${source}: ${String(error)}`)
|
|
168
|
+
});
|
|
169
|
+
routes.push({ path: routePath(relativeDir), source, html: content });
|
|
170
|
+
}
|
|
171
|
+
yield* Effect.forEach(entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")), (entry) => visit(path.join(directory, entry.name), path.join(relativeDir, entry.name), ownLayouts), { concurrency: 8, discard: true });
|
|
172
|
+
});
|
|
173
|
+
yield* visit(pagesRoot, "", []);
|
|
174
|
+
if (!routes.some((route) => route.path === "/")) {
|
|
175
|
+
yield* Effect.fail(new CompilerError(`${pagesRoot} requires index.html or index.md for the root route.`));
|
|
176
|
+
}
|
|
177
|
+
routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
178
|
+
return { routes, files };
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
export function compileProject(root) {
|
|
182
|
+
return Effect.runPromise(compileProjectEffect(root).pipe(Effect.provide(CompilerFileSystemLive)));
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compiler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAgC,MAAM,QAAQ,CAAC;AAarE,MAAM,WAAW,GAAG,qCAAqC,CAAC;AAE1D,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,GAAG,eAAe,CAAC;CACjC;AASD,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAA0B,yBAAyB,CAAC,CAAC;AAEtG,MAAM,EAAE,GAAG,CAAI,KAAa,EAAE,MAAwB,EAAE,EAAE,CACxD,MAAM,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,aAAa,CAAC,GAAG,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAExG,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE;IACtE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvE,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACzG,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,gBAAgB,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE;QACtD,IAAI,CAAC;YAAC,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAAC,CAAC;QAC3C,OAAO,KAAK,EAAE,CAAC;YACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;CAC+B,CAAC,CAAC;AAErC,SAAS,SAAS,CAAC,WAAmB;IACpC,OAAO,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACjF,CAAC;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,OAAe,EAAE,KAAa;IAC9D,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,wCAAwC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,UAAuC,EAAE,QAA2B,EAAE;IAC5G,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,MAAM,YAAY,GAAyD,EAAE,CAAC;IAC9E,MAAM,KAAK,GAAG,CAAC,IAAuC,EAAQ,EAAE;QAC9D,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC;YAAE,OAAO;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC;QAC1B,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC/F,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC;YACzC,IAAI,CAAC,QAAQ,EAAE,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,4BAA4B,CAAC,CAAC;YAC7G,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACtF,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAC/F,YAAY,CAAC,IAAI,CAAC;gBAChB,KAAK,EAAE,QAAQ,CAAC,WAAW;gBAC3B,GAAG,EAAE,QAAQ,CAAC,SAAS;gBACvB,KAAK,EAAE,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;aAC5D,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAAU;YAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YAChC,KAAK,MAAM,KAAK,IAAK,IAAyC,CAAC,OAAO,CAAC,UAAU;gBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAClG,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACpD,KAAK,MAAM,WAAW,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,MAAM,YAAY,GAAyD,EAAE,CAAC;IAC9E,MAAM,KAAK,GAAG,CAAC,IAAuC,EAAQ,EAAE;QAC9D,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC;YAAE,OAAO;QACjC,IAAI,IAAI,CAAC,OAAO,KAAK,YAAY,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC;YACzC,IAAI,CAAC,QAAQ,EAAE,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;YACtG,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;iBACnF,OAAO,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;YACpD,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC9G,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3G,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAAU;YAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YAChC,KAAK,MAAM,KAAK,IAAK,IAAyC,CAAC,OAAO,CAAC,UAAU;gBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAClG,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACpD,KAAK,MAAM,WAAW,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACzB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC;QACrC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAEtD,KAAK,MAAM,SAAS,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,CAAC;YACpD,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAChD,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE;gBAAE,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,GAAG,SAAS,uBAAuB,CAAC,CAAC,CAAC;QAC/G,CAAC;QAED,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC7C,MAAM,gBAAgB,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAC3D,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACrE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO;YAC7D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACrC,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/E,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,+BAA+B,KAAK,CAAC,IAAI,yDAAyD,CAAC,CAAC,CAAC;YAC5I,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACnD,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC,CAAC,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,CAAC,SAAiB,EAAE,WAAmB,EAAE,OAA0B,EAAsC,EAAE,CACvH,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YACvD,MAAM,kBAAkB,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,WAAW,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5F,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACpD,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAC5F,IAAI,cAAc,IAAI,iBAAiB,EAAE,CAAC;gBACxC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,yBAAyB,WAAW,MAAM,UAAU,QAAQ,kBAAkB,EAAE,CAAC,CAAC,CAAC;YAC1H,CAAC;YACD,MAAM,cAAc,GAAG,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC;YACxG,MAAM,UAAU,GAAG,cAAc;gBAC/B,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC9C,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC;YACjB,IAAI,cAAc;gBAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAE/C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YAChD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,OAAO,IAAI,WAAW;gBAAE,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,GAAG,SAAS,oCAAoC,CAAC,CAAC,CAAC;YACpH,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;gBAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;gBAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACnC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACnB,IAAI,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBAC5C,GAAG,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;oBACzC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,aAAa,CAAC,qBAAqB,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;iBACrF,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;gBACT,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBAC1B,GAAG,EAAE,GAAG,EAAE;wBACR,KAAK,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;4BAC5D,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAE,EAAE,OAAO,EAAE,UAAU,KAAK,GAAG,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC;wBACvF,CAAC;wBACD,OAAO,YAAY,CAAC,gBAAgB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;oBAC7D,CAAC;oBACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,aAAa,CAAC,0BAA0B,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;iBAC1F,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACvE,CAAC;YAED,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EACjG,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,EAClG,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEL,KAAK,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAChD,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,GAAG,SAAS,sDAAsD,CAAC,CAAC,CAAC;QAC5G,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAA4B,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { CompiledProject } from "./index.js";
|
|
3
|
+
export declare class IslandCompileError extends Error {
|
|
4
|
+
readonly _tag = "IslandCompileError";
|
|
5
|
+
}
|
|
6
|
+
export type IslandRenderer = (name: string, props: Record<string, unknown>, id: string) => Promise<string | undefined>;
|
|
7
|
+
/** Compiler phase that prerenders each known island into its route document. */
|
|
8
|
+
export declare function compileIslandsEffect(project: CompiledProject, renderer: IslandRenderer): Effect.Effect<{
|
|
9
|
+
routes: {
|
|
10
|
+
html: string;
|
|
11
|
+
path: string;
|
|
12
|
+
source: string;
|
|
13
|
+
}[];
|
|
14
|
+
files: readonly string[];
|
|
15
|
+
}, IslandCompileError, never>;
|
|
16
|
+
export declare function compileIslands(project: CompiledProject, renderer: IslandRenderer): Promise<CompiledProject>;
|
|
17
|
+
//# sourceMappingURL=islands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"islands.d.ts","sourceRoot":"","sources":["../../src/compiler/islands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AAEjE,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,wBAAwB;CACtC;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AA+CvH,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc;;;;;;;8BAyBtF;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAE3G"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { parseFragment } from "parse5";
|
|
3
|
+
export class IslandCompileError extends Error {
|
|
4
|
+
_tag = "IslandCompileError";
|
|
5
|
+
}
|
|
6
|
+
function locateIslands(html, routeId) {
|
|
7
|
+
const fragment = parseFragment(html, { sourceCodeLocationInfo: true });
|
|
8
|
+
const found = [];
|
|
9
|
+
const visit = (node, insideIsland = false) => {
|
|
10
|
+
if (!("tagName" in node))
|
|
11
|
+
return;
|
|
12
|
+
const isIsland = node.tagName === "fuzor-island";
|
|
13
|
+
if (isIsland) {
|
|
14
|
+
if (insideIsland)
|
|
15
|
+
throw new IslandCompileError("Nested Fuzor islands are not supported.");
|
|
16
|
+
const location = node.sourceCodeLocation;
|
|
17
|
+
if (!location?.startTag || !location.endTag) {
|
|
18
|
+
throw new IslandCompileError("A Fuzor island needs an explicit closing tag.");
|
|
19
|
+
}
|
|
20
|
+
const name = node.attrs.find((attribute) => attribute.name === "src")?.value;
|
|
21
|
+
if (!name)
|
|
22
|
+
throw new IslandCompileError("A Fuzor island needs a src attribute.");
|
|
23
|
+
const rawProps = node.attrs.find((attribute) => attribute.name === "props")?.value ?? "{}";
|
|
24
|
+
let props;
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(rawProps);
|
|
27
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
28
|
+
throw new Error("props must be an object");
|
|
29
|
+
props = parsed;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new IslandCompileError(`Invalid props for island ${name}: ${String(error)}`);
|
|
33
|
+
}
|
|
34
|
+
found.push({
|
|
35
|
+
name, props, id: `r${routeId}i${found.length}`,
|
|
36
|
+
start: location.startOffset, end: location.endOffset,
|
|
37
|
+
openEnd: location.startTag.endOffset, closeStart: location.endTag.startOffset
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
for (const child of node.childNodes)
|
|
41
|
+
visit(child, insideIsland || isIsland);
|
|
42
|
+
};
|
|
43
|
+
for (const node of fragment.childNodes)
|
|
44
|
+
visit(node);
|
|
45
|
+
return found;
|
|
46
|
+
}
|
|
47
|
+
/** Compiler phase that prerenders each known island into its route document. */
|
|
48
|
+
export function compileIslandsEffect(project, renderer) {
|
|
49
|
+
return Effect.gen(function* () {
|
|
50
|
+
const routes = yield* Effect.forEach(project.routes, (route, routeId) => Effect.gen(function* () {
|
|
51
|
+
const islands = yield* Effect.try({
|
|
52
|
+
try: () => locateIslands(route.html, routeId),
|
|
53
|
+
catch: (error) => error instanceof IslandCompileError ? error : new IslandCompileError(String(error))
|
|
54
|
+
});
|
|
55
|
+
let html = route.html;
|
|
56
|
+
const replacements = yield* Effect.forEach(islands, (island) => Effect.gen(function* () {
|
|
57
|
+
const preview = yield* Effect.tryPromise({
|
|
58
|
+
try: () => renderer(island.name, island.props, island.id),
|
|
59
|
+
catch: (error) => new IslandCompileError(`Cannot prerender ${island.name} in ${route.path}: ${String(error)}`)
|
|
60
|
+
});
|
|
61
|
+
if (preview === undefined)
|
|
62
|
+
return undefined;
|
|
63
|
+
const opening = route.html.slice(island.start, island.openEnd).replace(/>$/, ` data-fuzor-prerendered data-fuzor-island-id="${island.id}">`);
|
|
64
|
+
const closing = route.html.slice(island.closeStart, island.end);
|
|
65
|
+
return { start: island.start, end: island.end, html: opening + preview + closing };
|
|
66
|
+
}), { concurrency: 4 });
|
|
67
|
+
for (const replacement of replacements.filter((value) => value !== undefined).sort((a, b) => b.start - a.start)) {
|
|
68
|
+
html = html.slice(0, replacement.start) + replacement.html + html.slice(replacement.end);
|
|
69
|
+
}
|
|
70
|
+
return { ...route, html };
|
|
71
|
+
}), { concurrency: 4 });
|
|
72
|
+
return { ...project, routes };
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export function compileIslands(project, renderer) {
|
|
76
|
+
return Effect.runPromise(compileIslandsEffect(project, renderer));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=islands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"islands.js","sourceRoot":"","sources":["../../src/compiler/islands.ts"],"names":[],"mappings":"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"}
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare function createApp(directory: string | undefined, options?: {
|
|
2
|
+
yes?: boolean;
|
|
3
|
+
install?: boolean;
|
|
4
|
+
}): Promise<string>;
|
|
5
|
+
export declare const createCommand: import("citty").CommandDef<{
|
|
6
|
+
readonly directory: {
|
|
7
|
+
readonly type: "positional";
|
|
8
|
+
readonly description: "Directory for the new app";
|
|
9
|
+
readonly required: false;
|
|
10
|
+
};
|
|
11
|
+
readonly yes: {
|
|
12
|
+
readonly type: "boolean";
|
|
13
|
+
readonly description: "Use defaults without prompting";
|
|
14
|
+
};
|
|
15
|
+
readonly install: {
|
|
16
|
+
readonly type: "boolean";
|
|
17
|
+
readonly description: "Install dependencies with pnpm";
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
export declare function runCreateCli(): void;
|
|
21
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AA2BA,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAuDlI;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;;;;EAQxB,CAAC;AAEH,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
|