cirrojs 0.0.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 +82 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +1 -0
- package/dist/cli.sh +40 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +1 -0
- package/dist/router-CJ9aQRbJ.js +1 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +2 -0
- package/package.json +65 -0
- package/public/cli.sh +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 osawa-naotaka
|
|
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,82 @@
|
|
|
1
|
+
# cirro
|
|
2
|
+
|
|
3
|
+
React の島(islands)アーキテクチャによる、CSP 厳格(`unsafe-inline` 不要)を実現する軽量 static site generator。
|
|
4
|
+
|
|
5
|
+
## 特徴
|
|
6
|
+
|
|
7
|
+
- **インラインスクリプトを一切生成しない** → `script-src 'self'` の厳格な CSP で動作する(Astro 等が残しがちな少量のインラインを排除)
|
|
8
|
+
- 本文は**純粋な静的 HTML**、インタラクティブな箇所だけを**島**としてハイドレート(MPA)
|
|
9
|
+
- **Config Base Routing**: `.ts` に型付きオブジェクトでルート定義。動的ルートは `params → URL` の関数 + `getStaticPaths`
|
|
10
|
+
- **React のみ**(独自テンプレート構文の学習が不要)
|
|
11
|
+
|
|
12
|
+
## 使い方
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// vite.config.ts
|
|
16
|
+
import react from "@vitejs/plugin-react";
|
|
17
|
+
import { cirro } from "cirro/vite";
|
|
18
|
+
import { defineConfig } from "vite";
|
|
19
|
+
|
|
20
|
+
// React プラグインは利用者が明示的に追加する(cirro は内包しない)。
|
|
21
|
+
export default defineConfig({
|
|
22
|
+
plugins: [react(), cirro({ routes: "./src/routes.ts", islands: "./src/islands/registry.ts" })],
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```jsonc
|
|
27
|
+
// package.json
|
|
28
|
+
"scripts": { "dev": "cirro dev", "build": "tsc && cirro build", "preview": "vite preview" }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
CLI は `cirro dev`(SSR + ルーティング + HMR の開発サーバー)と `cirro build`(静的サイトを `dist/` へ生成)の 2 つ。プレビューは `vite preview` を使う。
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// src/islands/registry.ts — 島を登録(純データ)
|
|
35
|
+
import { Counter } from "./Counter";
|
|
36
|
+
export const islands = { counter: Counter } as const;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// src/islands/Island.ts — 型安全な <Island> を生成
|
|
41
|
+
import { createIsland } from "cirro";
|
|
42
|
+
import { islands } from "./registry";
|
|
43
|
+
export const Island = createIsland(islands);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
// src/routes.ts — ルート定義
|
|
48
|
+
import { route, type AnyRoute } from "cirro";
|
|
49
|
+
import { HomePage } from "./pages/home";
|
|
50
|
+
import { PostPage } from "./pages/post";
|
|
51
|
+
|
|
52
|
+
export const routes: AnyRoute[] = [
|
|
53
|
+
{ path: "/", component: HomePage },
|
|
54
|
+
route({
|
|
55
|
+
path: ({ slug }) => `/posts/${slug}`,
|
|
56
|
+
getStaticPaths: () => [{ slug: "hello" }],
|
|
57
|
+
component: PostPage,
|
|
58
|
+
}),
|
|
59
|
+
];
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
ページは完全な HTML 文書を返し、島は `<Island name="counter" props={{ initial: 3 }} />` のように型安全に配置する。
|
|
63
|
+
|
|
64
|
+
## Markdown
|
|
65
|
+
|
|
66
|
+
`createMarkdown()` でビルド時に Markdown を HTML 化する。`rehype-sanitize` を固定で強制し(ユーザープラグインは「サニタイズの上流」に積む)、目次抽出(`toc`)やクラスベースのシンタックスハイライト(`highlight`、インラインスタイルを出さず `style-src 'self'` と両立)に対応する。unified 一式はクライアントへ送られない。
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createMarkdown } from "cirro";
|
|
70
|
+
import remarkGfm from "remark-gfm";
|
|
71
|
+
|
|
72
|
+
const { render } = createMarkdown({ remarkPlugins: [remarkGfm], toc: true, highlight: true });
|
|
73
|
+
const { body, toc } = render(markdownSource); // body: サニタイズ済み HTML の React 要素 / toc: 目次
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## ドキュメント
|
|
77
|
+
|
|
78
|
+
使い方の詳細は [`doc/04_USAGE.md`](../../doc/04_USAGE.md) を参照(セットアップ、ルーティング、dev / build、島、Markdown、Panda CSS によるデザイン、CSP まで網羅)。島システムの仕組みは [`doc/03_ISLAND_SYSTEM.md`](../../doc/03_ISLAND_SYSTEM.md)、設計の背景は [`doc/01_CHARTER.md`](../../doc/01_CHARTER.md) を参照。
|
|
79
|
+
|
|
80
|
+
## ライセンス
|
|
81
|
+
|
|
82
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e,t}from"./router-CJ9aQRbJ.js";import{Fragment as n,createElement as r}from"react";import{renderToStaticMarkup as i}from"react-dom/server";import{dirname as a,join as o,resolve as s}from"node:path";import{mkdir as c,readFile as l,writeFile as u}from"node:fs/promises";import{build as d,createServer as f,createServerModuleRunner as p}from"vite";import{createServer as m}from"node:http";function h(e,t){return r(n,null,e,r(`script`,{async:!0,type:`module`,src:t,key:`cirro-client`}))}function g(e){for(let t of e.plugins){let e=t.api;if(t.name===`cirro`&&e?.options)return e.options}throw Error(`cirro: plugin not found in Vite config (did you add cirro() to plugins?)`)}async function _(){await d();let n=await f({server:{middlewareMode:!0,hmr:!1},appType:`custom`}),r=p(n.environments.ssr);try{let d=n.config,f=g(d),p=d.root,m=s(p,d.build.outDir),_=s(p,f.routes),v=JSON.parse(await l(o(m,`.vite/manifest.json`),`utf-8`))[`virtual:cirro/client`];if(!v)throw Error(`cirro: manifest entry "virtual:cirro/client" not found`);let y=`/${v.file}`,{routes:b}=await r.import(_);for(let n of t(b)){let t=`<!DOCTYPE html>${i(h(n.render(),y))}`,r=o(m,e(n.url));await c(a(r),{recursive:!0}),await u(r,t),console.log(`wrote ${r} (url: ${n.url})`)}}finally{await n.close()}}function v(e,t){let n=[t,t.replaceAll(`\\`,`/`)];for(let t of Object.values(e.environments)){let e=t.moduleGraph;if(!e)continue;let r=new Set,i=t=>{if(!r.has(t)){r.add(t),e.invalidateModule(t);for(let e of t.importers)i(e)}};for(let t of n){let n=e.getModulesByFile(t);if(n)for(let e of n)i(e)}}}async function y(e=5173){let n=await f({server:{middlewareMode:!0},appType:`custom`}),r=p(n.environments.ssr),o=g(n.config),c=n.config.root,l=s(c,o.routes),u=a(s(c,o.islands)).replaceAll(`\\`,`/`),d=m((e,a)=>{n.middlewares(e,a,async()=>{let o=e.url??`/`;try{let{routes:e}=await r.import(l),s=t(e),c=new URL(o,`http://localhost`).pathname.replace(/\/+$/,``)||`/`,u=s.find(e=>(e.url.replace(/\/+$/,``)||`/`)===c);if(!u){a.statusCode=404,a.setHeader(`Content-Type`,`text/html; charset=utf-8`),a.end(`<!DOCTYPE html><meta charset="utf-8"><h1>404 Not Found</h1>`);return}let d=`<!DOCTYPE html>${i(h(u.render(),`/@id/__x00__virtual:cirro/client`))}`;d=await n.transformIndexHtml(o,d),a.statusCode=200,a.setHeader(`Content-Type`,`text/html; charset=utf-8`),a.end(d)}catch(e){a.statusCode=500,a.end(String(e))}})}),_=`${s(c,o.watchDir??`./src`).replaceAll(`\\`,`/`).replace(/\/+$/,``)}/`;n.watcher.on(`change`,e=>{let t=e.replaceAll(`\\`,`/`);t.startsWith(u)||t.startsWith(_)&&(v(n,e),n.ws.send({type:`full-reload`}))}),d.listen(e,()=>{console.log(`cirro dev: http://localhost:${e}`)})}async function b(e){let t=e[0];t===`dev`?await y():t===`build`?await _():(console.error(`usage: cirro <dev|build>`),process.exit(1))}export{b as main};
|
package/dist/cli.sh
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
runtime=""
|
|
4
|
+
script_args=()
|
|
5
|
+
|
|
6
|
+
# 引数をループして実行環境とその他の引数を分離
|
|
7
|
+
for arg in "$@"; do
|
|
8
|
+
case "$arg" in
|
|
9
|
+
--bun) runtime="bun" ;;
|
|
10
|
+
--node) runtime="node" ;;
|
|
11
|
+
--deno) runtime="deno" ;;
|
|
12
|
+
*) script_args+=("$arg") ;;
|
|
13
|
+
esac
|
|
14
|
+
done
|
|
15
|
+
|
|
16
|
+
# 実行環境が指定されていない場合、自動検出(優先順位: bun -> node -> deno)
|
|
17
|
+
if [ -z "$runtime" ]; then
|
|
18
|
+
if command -v bun &> /dev/null; then
|
|
19
|
+
runtime="bun"
|
|
20
|
+
elif command -v node &> /dev/null; then
|
|
21
|
+
runtime="node"
|
|
22
|
+
elif command -v deno &> /dev/null; then
|
|
23
|
+
runtime="deno"
|
|
24
|
+
else
|
|
25
|
+
echo "Error: No JavaScript runtime found (bun, node, or deno)" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
case "$runtime" in
|
|
31
|
+
bun)
|
|
32
|
+
exec bun -e='import { main } from "cirro/bin"; await main(process.argv.slice(1));' "${script_args[@]}"
|
|
33
|
+
;;
|
|
34
|
+
node)
|
|
35
|
+
exec node --eval 'import { main } from "cirro/bin"; await main(process.argv.slice(1));' "${script_args[@]}"
|
|
36
|
+
;;
|
|
37
|
+
deno)
|
|
38
|
+
exec deno eval 'import { main } from "cirro/bin"; await main(process.argv.slice(2));' "${script_args[@]}"
|
|
39
|
+
;;
|
|
40
|
+
esac
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ComponentProps, ComponentType, ReactElement } from "react";
|
|
2
|
+
import { ToC, ToC as ToC$1 } from "remark-export-toc";
|
|
3
|
+
import { PluggableList } from "unified";
|
|
4
|
+
import { Schema } from "hast-util-sanitize";
|
|
5
|
+
|
|
6
|
+
//#region src/island.d.ts
|
|
7
|
+
type IslandRegistry = Record<string, ComponentType<any>>;
|
|
8
|
+
declare function createIsland<R extends IslandRegistry>(islands: R): <K extends keyof R & string>({
|
|
9
|
+
name,
|
|
10
|
+
props
|
|
11
|
+
}: {
|
|
12
|
+
name: K;
|
|
13
|
+
props: ComponentProps<R[K]>;
|
|
14
|
+
}) => import("react").DetailedReactHTMLElement<{
|
|
15
|
+
"data-island": K;
|
|
16
|
+
"data-props": string;
|
|
17
|
+
dangerouslySetInnerHTML: {
|
|
18
|
+
__html: string;
|
|
19
|
+
};
|
|
20
|
+
}, HTMLElement>;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/markdown.d.ts
|
|
23
|
+
interface MarkdownConfig {
|
|
24
|
+
remarkPlugins?: PluggableList;
|
|
25
|
+
rehypePlugins?: PluggableList;
|
|
26
|
+
sanitizeSchema?: (defaults: Schema) => Schema;
|
|
27
|
+
toc?: boolean | {
|
|
28
|
+
prefix?: string;
|
|
29
|
+
startLevel?: number;
|
|
30
|
+
};
|
|
31
|
+
highlight?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface RenderResult {
|
|
34
|
+
body: ReactElement;
|
|
35
|
+
toc: ToC$1[];
|
|
36
|
+
}
|
|
37
|
+
declare function createMarkdown(config?: MarkdownConfig): {
|
|
38
|
+
Markdown: ({
|
|
39
|
+
source,
|
|
40
|
+
className
|
|
41
|
+
}: {
|
|
42
|
+
source: string;
|
|
43
|
+
className?: string;
|
|
44
|
+
}) => ReactElement;
|
|
45
|
+
render: (source: string, options?: {
|
|
46
|
+
className?: string;
|
|
47
|
+
}) => RenderResult;
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/router.d.ts
|
|
51
|
+
type Params = Record<string, string>;
|
|
52
|
+
type StaticRoute = {
|
|
53
|
+
path: string;
|
|
54
|
+
component: (props: {
|
|
55
|
+
params: Record<string, never>;
|
|
56
|
+
}) => ReactElement;
|
|
57
|
+
};
|
|
58
|
+
type DynamicRoute<P extends Params = Params> = {
|
|
59
|
+
path: (params: P) => string;
|
|
60
|
+
getStaticPaths: () => P[];
|
|
61
|
+
component: (props: {
|
|
62
|
+
params: P;
|
|
63
|
+
}) => ReactElement;
|
|
64
|
+
};
|
|
65
|
+
type AnyRoute = StaticRoute | DynamicRoute<any>;
|
|
66
|
+
declare function route<P extends Params>(def: DynamicRoute<P>): DynamicRoute<P>;
|
|
67
|
+
declare function route(def: StaticRoute): StaticRoute;
|
|
68
|
+
type ResolvedPage = {
|
|
69
|
+
url: string;
|
|
70
|
+
render: () => ReactElement;
|
|
71
|
+
};
|
|
72
|
+
declare function expandRoutes(routes: AnyRoute[]): ResolvedPage[];
|
|
73
|
+
declare function urlToFilePath(url: string): string;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { type AnyRoute, type DynamicRoute, type MarkdownConfig, type Params, type RenderResult, type ResolvedPage, type StaticRoute, type ToC, createIsland, createMarkdown, expandRoutes, route, urlToFilePath };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t,t as n}from"./router-CJ9aQRbJ.js";import{createElement as r}from"react";import{renderToString as i}from"react-dom/server";import a from"rehype-prism";import o,{defaultSchema as s}from"rehype-sanitize";import c from"rehype-stringify";import l from"remark-export-toc";import u from"remark-parse";import d from"remark-rehype";import{unified as f}from"unified";import{jsx as p}from"react/jsx-runtime";function m(e){return function({name:t,props:n}){let a=i(r(e[t],n));return r(`div`,{"data-island":t,"data-props":JSON.stringify(n),dangerouslySetInnerHTML:{__html:a}})}}function h(e={}){let t={prefix:`heading`,startLevel:2,...typeof e.toc==`object`?e.toc:{}},n={...s,clobber:(s.clobber??[]).filter(e=>e!==`id`)},r=e.sanitizeSchema?e.sanitizeSchema(n):n,i=f().use(u).use(e.remarkPlugins??[]).use(e.toc?[[l,t]]:[]).use(d).use(e.rehypePlugins??[]).use(o,r).use(e.highlight?[a]:[]).use(c).freeze();function m(e,t){return p(`div`,{className:t,dangerouslySetInnerHTML:{__html:e}})}function h(e,t){let n=i.processSync(e),r=n.data.toc??[];return{body:m(String(n),t?.className),toc:r}}function g({source:e,className:t}){return h(e,{className:t}).body}return{Markdown:g,render:h}}export{m as createIsland,h as createMarkdown,n as expandRoutes,e as route,t as urlToFilePath};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return e}function t(e){let t=[];for(let n of e)if(`getStaticPaths`in n)for(let e of n.getStaticPaths())t.push({url:n.path(e),render:()=>n.component({params:e})});else t.push({url:n.path,render:()=>n.component({params:{}})});return t}function n(e){return e===`/`?`index.html`:`${e.replace(/^\/+|\/+$/g,``)}/index.html`}export{e as n,n as r,t};
|
package/dist/vite.d.ts
ADDED
package/dist/vite.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{resolve as e}from"node:path";const t=`virtual:cirro/client`,n=`\0${t}`;function r(r){let i=r.islands;return{name:`cirro`,api:{options:r},config(){return{build:{manifest:!0,modulePreload:{polyfill:!1},assetsInlineLimit:0,rollupOptions:{input:{client:t}}}}},configResolved(t){if(i=e(t.root,r.islands),!t.plugins.some(e=>e.name?.startsWith(`vite:react`)))throw Error(`cirro: React プラグインが見つかりません。vite.config の plugins に react()(@vitejs/plugin-react)を cirro() より前に追加してください。`)},resolveId(e){if(e===t)return n},load(e){if(e===n)return[`import { createElement } from "react";`,`import { hydrateRoot } from "react-dom/client";`,`import { islands } from ${JSON.stringify(i)};`,``,`for (const el of document.querySelectorAll("[data-island]")) {`,` const name = el.dataset.island;`,` if (!name || !(name in islands)) continue;`,` const props = el.dataset.props ? JSON.parse(el.dataset.props) : {};`,` hydrateRoot(el, createElement(islands[name], props));`,`}`].join(`
|
|
2
|
+
`)}}}export{r as cirro};
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cirrojs",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React islands SSG with strict CSP (no unsafe-inline). Vite-based, MPA-first.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.ts",
|
|
10
|
+
"./vite": "./src/vite.ts",
|
|
11
|
+
"./bin": "./src/cli.ts"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"cirro": "./public/cli.sh"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./vite": {
|
|
23
|
+
"types": "./dist/vite.d.ts",
|
|
24
|
+
"default": "./dist/vite.js"
|
|
25
|
+
},
|
|
26
|
+
"./bin": {
|
|
27
|
+
"types": "./dist/cli.d.ts",
|
|
28
|
+
"default": "./dist/cli.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"cirro": "./dist/cli.sh"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsdown",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
38
|
+
"format": "biome check --write ./src"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"hast-util-sanitize": "^5.0.2",
|
|
42
|
+
"rehype-prism": "^2.3.3",
|
|
43
|
+
"rehype-sanitize": "^6.0.0",
|
|
44
|
+
"rehype-stringify": "^10.0.1",
|
|
45
|
+
"remark-export-toc": "0.1.0-alpha.2",
|
|
46
|
+
"remark-parse": "^11.0.0",
|
|
47
|
+
"remark-rehype": "^11.1.2",
|
|
48
|
+
"unified": "^11.0.5"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
52
|
+
"react": "^19.2.7",
|
|
53
|
+
"react-dom": "^19.2.7",
|
|
54
|
+
"vite": "^8.0.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@biomejs/biome": "2.5.0",
|
|
58
|
+
"@types/node": "25.9.3",
|
|
59
|
+
"@types/react": "19.2.17",
|
|
60
|
+
"@types/react-dom": "19.2.3",
|
|
61
|
+
"tsdown": "^0.22.2",
|
|
62
|
+
"typescript": "6.0.3",
|
|
63
|
+
"vite": "8.0.16"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/public/cli.sh
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
runtime=""
|
|
4
|
+
script_args=()
|
|
5
|
+
|
|
6
|
+
# 引数をループして実行環境とその他の引数を分離
|
|
7
|
+
for arg in "$@"; do
|
|
8
|
+
case "$arg" in
|
|
9
|
+
--bun) runtime="bun" ;;
|
|
10
|
+
--node) runtime="node" ;;
|
|
11
|
+
--deno) runtime="deno" ;;
|
|
12
|
+
*) script_args+=("$arg") ;;
|
|
13
|
+
esac
|
|
14
|
+
done
|
|
15
|
+
|
|
16
|
+
# 実行環境が指定されていない場合、自動検出(優先順位: bun -> node -> deno)
|
|
17
|
+
if [ -z "$runtime" ]; then
|
|
18
|
+
if command -v bun &> /dev/null; then
|
|
19
|
+
runtime="bun"
|
|
20
|
+
elif command -v node &> /dev/null; then
|
|
21
|
+
runtime="node"
|
|
22
|
+
elif command -v deno &> /dev/null; then
|
|
23
|
+
runtime="deno"
|
|
24
|
+
else
|
|
25
|
+
echo "Error: No JavaScript runtime found (bun, node, or deno)" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
case "$runtime" in
|
|
31
|
+
bun)
|
|
32
|
+
exec bun -e='import { main } from "cirro/bin"; await main(process.argv.slice(1));' "${script_args[@]}"
|
|
33
|
+
;;
|
|
34
|
+
node)
|
|
35
|
+
exec node --eval 'import { main } from "cirro/bin"; await main(process.argv.slice(1));' "${script_args[@]}"
|
|
36
|
+
;;
|
|
37
|
+
deno)
|
|
38
|
+
exec deno eval 'import { main } from "cirro/bin"; await main(process.argv.slice(2));' "${script_args[@]}"
|
|
39
|
+
;;
|
|
40
|
+
esac
|