nene-lang 0.0.0-stage → 0.1.0

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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +219 -2
  3. package/bin/nene.js +13 -0
  4. package/build/checker.d.ts +103 -0
  5. package/build/checker.js +1549 -0
  6. package/build/cli.d.ts +2 -0
  7. package/build/cli.js +202 -0
  8. package/build/codegen.d.ts +3 -0
  9. package/build/codegen.js +273 -0
  10. package/build/compiler.d.ts +30 -0
  11. package/build/compiler.js +178 -0
  12. package/build/css.d.ts +11 -0
  13. package/build/css.js +249 -0
  14. package/build/exhaust.d.ts +2 -0
  15. package/build/exhaust.js +115 -0
  16. package/build/lexer.d.ts +35 -0
  17. package/build/lexer.js +227 -0
  18. package/build/lib/App.nn +27 -0
  19. package/build/lib/Cmd.nn +5 -0
  20. package/build/lib/Dict.nn +54 -0
  21. package/build/lib/Dom.nn +13 -0
  22. package/build/lib/Env.nn +7 -0
  23. package/build/lib/Http.nn +21 -0
  24. package/build/lib/Json.nn +12 -0
  25. package/build/lib/Keyboard.nn +6 -0
  26. package/build/lib/Layout.nn +49 -0
  27. package/build/lib/List.nn +54 -0
  28. package/build/lib/Maybe.nn +18 -0
  29. package/build/lib/Nav.nn +7 -0
  30. package/build/lib/Prelude.nn +99 -0
  31. package/build/lib/Random.nn +15 -0
  32. package/build/lib/Result.nn +26 -0
  33. package/build/lib/Storage.nn +12 -0
  34. package/build/lib/String.nn +24 -0
  35. package/build/lib/Sub.nn +5 -0
  36. package/build/lib/Time.nn +9 -0
  37. package/build/lib/Window.nn +9 -0
  38. package/build/lsp.d.ts +2 -0
  39. package/build/lsp.js +300 -0
  40. package/build/packages.d.ts +5 -0
  41. package/build/packages.js +58 -0
  42. package/build/parser.d.ts +6 -0
  43. package/build/parser.js +997 -0
  44. package/build/report.d.ts +10 -0
  45. package/build/report.js +66 -0
  46. package/build/syntax.d.ts +325 -0
  47. package/build/syntax.js +22 -0
  48. package/build/types.d.ts +86 -0
  49. package/build/types.js +396 -0
  50. package/build/vite.d.ts +5 -0
  51. package/build/vite.js +75 -0
  52. package/docs/design.md +114 -0
  53. package/docs/guide.md +452 -0
  54. package/docs/spec.md +722 -0
  55. package/editors/nvim/ftdetect/nene.vim +1 -0
  56. package/editors/nvim/ftplugin/nene.vim +10 -0
  57. package/editors/nvim/lua/nene.lua +92 -0
  58. package/editors/nvim/syntax/nene.vim +71 -0
  59. package/package.json +64 -4
  60. package/runtime/http.js +104 -0
  61. package/runtime/runtime.js +464 -0
  62. package/template/.claude/hooks/check.mjs +21 -0
  63. package/template/.claude/settings.json +18 -0
  64. package/template/CLAUDE.md +58 -0
  65. package/template/README.md +33 -0
  66. package/template/gitignore +3 -0
  67. package/template/index.html +16 -0
  68. package/template/package.json +17 -0
  69. package/template/public/nene.svg +12 -0
  70. package/template/src/Main.nn +145 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kimuracoki
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 CHANGED
@@ -1,3 +1,220 @@
1
- # Temporary Holding Version
1
+ # Nene
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Simple rules. Beautiful structures.**
4
+
5
+ Nene(ネネ)は、Haskell の書き心地と JSX の見やすさを両立させる、UI 向けの関数型言語。少ない規則で書き、タグを見れば画面の形が分かる。
6
+
7
+ > **ステータス: v0.1。** 言語仕様、コンパイラ、ランタイム、`nene` コマンド、Vite プラグイン、公式の部品集 [nene-ui](ui/README.md)、nvim 用のハイライトと言語サーバーがある。
8
+
9
+ ```haskell
10
+ view :: Model -> Html Msg
11
+ view n =
12
+ <Column gap={M} align={Center} class={css.page}>
13
+ <h1>カウンター</h1>
14
+ <p class={count}>{show n}</p>
15
+ <Row gap={S}>
16
+ <Button onClick={Decrement} disabled={atZero}>-</Button>
17
+ <Button onClick={Increment}>+</Button>
18
+ </Row>
19
+ </Column>
20
+ where
21
+ atZero = n == 0
22
+
23
+ count
24
+ | n >= 10 = classes [css.count, css.big]
25
+ | otherwise = css.count
26
+
27
+
28
+ style
29
+ .page { padding: var(--space-xl); }
30
+ .count {
31
+ font-size: 2.5rem;
32
+ font-weight: 700;
33
+ }
34
+ .big { color: crimson; }
35
+ ```
36
+
37
+ ## 原則
38
+
39
+ 1. **タグを見れば、画面に何がどう並ぶかが分かる。** 並び方は `<Row>` `<Column>` `<Grid>` `<Stack>` で書く。
40
+ 2. **タグには名前だけを書く。** 計算・分岐は `where` に、見た目はファイル末尾の `style` ブロックに普通の CSS で書く。JSX の `{}` に演算子や `if` を書くとコンパイルエラーになる。
41
+ 3. **書き方は一つ。** `case` `if` `let` ラムダはなく、等式のパターンマッチ・ガード・`where` で書く。CSS の当て方も `class={css.名前}` だけ。
42
+ 4. **型が通れば、実行時エラーは起きない。** 静的型、行多相のレコード、パターンの網羅性チェック(漏れはエラー)。
43
+
44
+ ## 構成
45
+
46
+ | 層 | 中身 | パッケージ |
47
+ | --- | --- | --- |
48
+ | 言語 | コンパイラ、ランタイム、`nene` コマンド、標準ライブラリ(ルーティング・HTTP・JSON など) | `nene-lang` |
49
+ | 部品集 | ボタン・入力欄・ダイアログなど 16 の部品 | [`nene-ui`](ui/README.md) |
50
+
51
+ 標準ライブラリは差し替えられない(ルーティングも HTTP も一通りだけ)。部品集は別のパッケージで、使わなくてもよい。出力には、アプリが使っている標準モジュールとランタイムの部分だけが入る。
52
+
53
+ ## ドキュメント
54
+
55
+ | | 内容 |
56
+ | --- | --- |
57
+ | [docs/guide.md](docs/guide.md) | **入門。** 最初に読むもの。画面を作りながら書き方を一通り見る |
58
+ | [docs/spec.md](docs/spec.md) | 言語仕様。すべての規則を正確に書いた参照用 |
59
+ | [docs/design.md](docs/design.md) | 設計の考え方。なぜこういう言語なのか、何を入れなかったのか |
60
+
61
+ ## 動機
62
+
63
+ Elm でサンプルアプリを作ってみて感じた不満から始まっている。
64
+
65
+ | 不満 | Nene での方針 |
66
+ | --- | --- |
67
+ | `case` と `let ... in` しかなく冗長 | Haskell と同じく、複数等式による関数定義・ガード・`where` を使える |
68
+ | view が `div [ ... ] [ ... ]` で、UI の形が読み取りにくい | JSX を式としてそのまま書ける |
69
+ | 横並び・縦並びが CSS に隠れていて、マークアップを見てもレイアウトが分からない | 並び方はタグで書く。見た目は同じファイルの `style` ブロックに CSS で書く |
70
+ | マークアップの中に分岐や計算が混ざると読みにくい | JSX の `{}` には名前だけを書ける。処理は `where` に書く |
71
+ | コンポーネントの省略可能な引数がなく、呼び出しが長い | props レコードにデフォルト値を持たせられる |
72
+ | 型の安全性・実行時エラーの少なさは手放したくない | 静的型・ADT・網羅性チェックは維持する |
73
+
74
+ ## はじめる
75
+
76
+ Node.js 20.19 以上が必要。
77
+
78
+ ```sh
79
+ npm create nene-lang my-app
80
+ cd my-app
81
+ npm install
82
+ npm run dev # http://localhost:5173/ 。保存すると自動で再読み込み
83
+ ```
84
+
85
+ 設定ファイルはいらない。
86
+
87
+ ```
88
+ my-app/
89
+ package.json
90
+ index.html <script type="module" src="/src/Main.nn"> が入口
91
+ src/Main.nn
92
+ public/ 画像など。そのままの名前で配信される
93
+ CLAUDE.md Claude Code 向けの Nene の書き方(nene-ui の部品も)
94
+ .claude/ .nn を編集したら型検査して、エラーを Claude に伝えるフック
95
+ ```
96
+
97
+ | コマンド | 内容 |
98
+ | --- | --- |
99
+ | `npm run dev`(`nene dev`) | 開発サーバー。コンパイルエラーはブラウザに場所つきで出る |
100
+ | `npm run build`(`nene build`) | `dist/` に出力する。そのまま静的ホスティングに置ける。`--base /repo/` でサブディレクトリにも置ける |
101
+ | `npm run preview`(`nene preview`) | `dist/` を手元で動かして確かめる |
102
+ | `npm run check`(`nene check`) | 型検査だけ。エラーは該当する行つきで出る |
103
+
104
+ `dev`・`build`・`preview` は内部で Vite を使う。複数ページなど特別なことをしたいときだけ、普通の `vite.config.ts` を置けば読み込まれる(例: `examples/vite.config.ts`)。
105
+
106
+ ### npm に公開する前に、手元で試す
107
+
108
+ `npm create nene-lang` の代わりに、このリポジトリを `npm link` する。
109
+
110
+ ```sh
111
+ cd /path/to/nene
112
+ npm install
113
+ npm link # nene コマンドが使えるようになる
114
+
115
+ cd ~/どこか
116
+ nene new my-app # 手元のリポジトリを使うプロジェクトとして作られる
117
+ cd my-app
118
+ npm install
119
+ npm run dev
120
+ ```
121
+
122
+ 手元のリポジトリから作ったプロジェクトは、`package.json` の依存先が `file:/path/to/nene` になる。コンパイラを直すと、そのまま反映される(ビルドは不要)。やめるときは `npm unlink -g nene-lang`。
123
+
124
+ ### 公開する
125
+
126
+ `nene build` は、`/todos/3` のような URL を直接開いても表示されるように、`404.html`(GitHub Pages など)と `_redirects`(Netlify・Cloudflare Pages)も出力する。ホスティングごとの手順は、作ったプロジェクトの `README.md` に書いてある。
127
+
128
+ ### パッケージを作る
129
+
130
+ `package.json` に `"nene": { "source": "src" }` と書いた npm パッケージは、`src/` の `.nn` を `import` で使える(`nene-ui` もこの形)。モジュール名は、ほかとぶつからないように頭を揃える(`Ui.Button` など)。同じ名前のモジュールが 2 か所にあるとエラーになる。
131
+
132
+ ### 既存の Vite プロジェクトに入れる
133
+
134
+ ```ts
135
+ // vite.config.ts
136
+ import nene from "nene-lang/vite";
137
+
138
+ export default { plugins: [nene()] };
139
+ ```
140
+
141
+ ## nvim
142
+
143
+ nvim 0.11 以上。プラグインを入れて `setup()` を呼ぶだけ。言語サーバーは、プロジェクトの `node_modules` にある `nene` を自動で使う。
144
+
145
+ ```lua
146
+ -- lazy.nvim
147
+ {
148
+ "kimuracoki/nene",
149
+ config = function(plugin)
150
+ vim.opt.rtp:append(plugin.dir .. "/editors/nvim")
151
+ require("nene").setup()
152
+ end,
153
+ }
154
+
155
+ -- 手元のリポジトリを使う場合
156
+ vim.opt.rtp:append("/path/to/nene/editors/nvim")
157
+ require("nene").setup()
158
+ ```
159
+
160
+ - `.nn` のハイライト。言語サーバーが動いているときは、その解析結果で色を分ける(部品と HTML 要素、コンストラクタ、フィールド、引数など)
161
+ - 入力中のエラー表示(1 ファイルのエラーをまとめて表示)
162
+ - `K` で型を表示、`gd` で定義へ移動(`<Button>` から `Button.nn` へも飛べる)
163
+ - 補完
164
+ - `<But` → `<Button onClick={…}>…</Button>`(必須の属性と閉じタグまで入る)
165
+ - タグの中で属性名(まだ書いていないもの。必須のものが先)
166
+ - `import ` の後でプロジェクトのモジュール名、`List.` の後でその中の名前、`model.` の後でフィールド名
167
+
168
+ 補完は、nvim-cmp や blink.cmp が入っていればそちらに出て、なければ nvim 標準の補完を使う。
169
+
170
+ ## この言語自体を開発する
171
+
172
+ ```sh
173
+ npm install
174
+ npm test # テスト
175
+ npm run dev # examples/ を開く
176
+ npm run build:package # 配布用の JavaScript を build/ に出力する(npm publish の前に自動で実行される)
177
+ npm pack # 配布するパッケージを手元で作る
178
+ ```
179
+
180
+ 開発中は TypeScript をビルドせずにそのまま実行する(Node.js 23.6 以上)。配布するときだけ `build/` に JavaScript を出力する。npm は `node_modules` の中の `.ts` を実行できないため。
181
+
182
+ ## サンプル
183
+
184
+ | ファイル | 内容 |
185
+ | --- | --- |
186
+ | [examples/Counter.nn](examples/Counter.nn) | 最小のカウンター |
187
+ | [examples/Todo.nn](examples/Todo.nn) | elm-sample のタスク管理画面に相当するもの |
188
+ | [examples/Gallery.nn](examples/Gallery.nn) | アルバムごとに写真が横に続くギャラリー。開いた写真は URL に入り、戻るボタンや Esc で閉じる |
189
+ | [examples/Stopwatch.nn](examples/Stopwatch.nn) | タイマー・キーボード・ブラウザへの保存・ダークテーマを使う例 |
190
+ | [examples/Showcase.nn](examples/Showcase.nn) | nene-ui の部品を一通り並べた見本 |
191
+
192
+ ## 構成
193
+
194
+ | パス | 内容 |
195
+ | --- | --- |
196
+ | `src/lexer.ts` | 字句解析 |
197
+ | `src/parser.ts` | 構文解析。JSX は文字単位で読み、レイアウト(インデント)はトップレベルと `where` だけで使う |
198
+ | `src/types.ts` | 型の表現と単一化(行多相レコード、組み込み型クラス) |
199
+ | `src/checker.ts` | 名前解決と型推論(Hindley–Milner、束縛のまとまりごとに汎化) |
200
+ | `src/exhaust.ts` | パターンの網羅性チェック |
201
+ | `src/codegen.ts` | JavaScript の生成 |
202
+ | `src/compiler.ts` | モジュールを依存順に読み込み、1 つの JavaScript にまとめる(使っている標準モジュールだけを入れる) |
203
+ | `src/packages.ts` | `node_modules` から Nene のパッケージを探す |
204
+ | `src/lib/*.nn` | 標準ライブラリ(Nene で書かれている) |
205
+ | `runtime/runtime.js` | 仮想 DOM、レイアウトタグ、`style` ブロックの CSS の読み込み、URL、The Elm Architecture の実行 |
206
+ | `runtime/http.js` | JSON との変換と HTTP(使うアプリにだけ入る) |
207
+ | `ui/` | 公式の部品集 `nene-ui` |
208
+ | `src/css.ts` | `style` ブロックのクラス名を取り出し、ファイルごとに区別できる名前に書き換える |
209
+ | `src/cli.ts` / `src/vite.ts` / `src/lsp.ts` | `nene` コマンド、Vite プラグイン、言語サーバー |
210
+ | `src/report.ts` | コンパイルエラーを、該当する行と印つきで見せる |
211
+ | `template/` | `nene new` で作るプロジェクトのひな形 |
212
+ | `bin/nene.js` | コマンドの入口。手元のリポジトリでは `src/` を、npm から入れたときは `build/` を使う |
213
+ | `editors/nvim/` | nvim 用のファイル種別判定・ハイライト・設定 |
214
+
215
+ ## まだないもの
216
+
217
+ - 部品の中だけで完結する状態(React の useState に相当するもの)
218
+ - サーバー側での描画(SSR)と、ビルド時に HTML を作る静的生成
219
+ - VS Code の拡張
220
+ - Tree-sitter の文法(今は Vim の syntax ファイルと、言語サーバーによる色分け)
package/bin/nene.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ // nene コマンドの入口。
3
+ // 手元のリポジトリ(src/ がある)から動かすときは TypeScript のソースをそのまま使い、
4
+ // npm から入れたときはビルド済みの build/ を使う。
5
+ import { existsSync } from "node:fs";
6
+
7
+ const source = new URL("../src/cli.ts", import.meta.url);
8
+ const canStripTypes = process.features?.typescript !== undefined && process.features.typescript !== false;
9
+ if (existsSync(source) && canStripTypes) {
10
+ await import(source.href);
11
+ } else {
12
+ await import(new URL("../build/cli.js", import.meta.url).href);
13
+ }
@@ -0,0 +1,103 @@
1
+ import { type Expr, type Module, type Pos, type ValueRef, type CtorRef } from "./syntax.ts";
2
+ import { type Scheme, type Type } from "./types.ts";
3
+ import { type ScopedStyle } from "./css.ts";
4
+ export interface ValueInfo {
5
+ scheme: Scheme;
6
+ ref: ValueRef;
7
+ def?: Location;
8
+ param?: boolean;
9
+ }
10
+ export interface Location {
11
+ file: string;
12
+ pos: Pos;
13
+ }
14
+ export type SemType = "namespace" | "type" | "class" | "enumMember" | "function" | "variable" | "parameter" | "property" | "macro";
15
+ export interface SemToken {
16
+ pos: Pos;
17
+ length: number;
18
+ type: SemType | (() => SemType);
19
+ }
20
+ export interface TagCandidate {
21
+ name: string;
22
+ detail: string;
23
+ fields: {
24
+ name: string;
25
+ required: boolean;
26
+ type: string;
27
+ }[];
28
+ children: boolean;
29
+ }
30
+ export interface CompletionInfo {
31
+ values: {
32
+ name: string;
33
+ detail: string;
34
+ }[];
35
+ quals: Map<string, {
36
+ name: string;
37
+ detail: string;
38
+ }[]>;
39
+ tags: TagCandidate[];
40
+ }
41
+ export interface HoverInfo {
42
+ pos: Pos;
43
+ length: number;
44
+ name: string;
45
+ type: () => string;
46
+ typeOf?: () => Type;
47
+ label?: () => string;
48
+ def?: Location | (() => Location | undefined);
49
+ }
50
+ export interface CtorInfo {
51
+ ref: CtorRef;
52
+ scheme: Scheme;
53
+ def?: Location;
54
+ }
55
+ export interface TypeInfo {
56
+ name: string;
57
+ display: string;
58
+ arity: number;
59
+ kind: "data" | "alias" | "opaque";
60
+ ctors: string[];
61
+ expand?: (args: Type[]) => Type;
62
+ defaults?: Set<string>;
63
+ defaultsKey?: string;
64
+ def?: Location;
65
+ params?: string[];
66
+ }
67
+ export interface ModuleIface {
68
+ name: string;
69
+ file: string;
70
+ pos: Pos;
71
+ js: string;
72
+ values: Map<string, ValueInfo>;
73
+ ctors: Map<string, CtorInfo>;
74
+ types: Map<string, TypeInfo>;
75
+ }
76
+ export declare const STD_QUALIFIED: string[];
77
+ export declare const RESERVED_MODULES: string[];
78
+ export declare const HTML_ELEMENTS: Set<string>;
79
+ export declare const HTML_ATTRS: string[];
80
+ export declare function jsName(name: string): string;
81
+ export declare function fieldKey(name: string): string;
82
+ export declare function moduleJs(name: string): string;
83
+ export declare function uniqueTypeName(module: string, name: string): string;
84
+ export interface CheckOptions {
85
+ deps: Map<string, ModuleIface>;
86
+ builtin: boolean;
87
+ entry: boolean;
88
+ }
89
+ export interface CheckResult {
90
+ iface: ModuleIface;
91
+ defaults: {
92
+ key: string;
93
+ fields: {
94
+ name: string;
95
+ expr: Expr;
96
+ }[];
97
+ }[];
98
+ hovers: HoverInfo[];
99
+ tokens: SemToken[];
100
+ completion: CompletionInfo;
101
+ style: ScopedStyle | null;
102
+ }
103
+ export declare function checkModule(mod: Module, file: string, opts: CheckOptions): CheckResult;