noshift.js 0.3.0 → 0.4.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.
- package/README-ja.md +86 -33
- package/README.md +87 -33
- package/bin/cli.js +67 -11
- package/commands/clean.js +3 -3
- package/commands/compile.js +28 -10
- package/commands/create.js +80 -104
- package/commands/dev.js +11 -8
- package/commands/init.js +11 -5
- package/commands/run.js +15 -2
- package/package.json +2 -3
- package/src/config.js +8 -6
- package/src/convert.js +245 -8
- package/src/logger.js +1 -3
package/README-ja.md
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://www.npmjs.com/package/noshift.js) [](./LICENSE)
|
|
2
|
+
|
|
3
|
+
# NoShift.js
|
|
4
|
+
|
|
5
|
+
<div align="center">
|
|
6
|
+
<img src="https://raw.githubusercontent.com/otoneko1102/NoShift.js/refs/heads/main/icon.png" alt="noshift.js" width="128" height="128">
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div align="center">
|
|
10
|
+
|
|
11
|
+
[English](./README.md) | **日本語**
|
|
12
|
+
|
|
13
|
+
</div>
|
|
2
14
|
|
|
3
15
|
> Shift キーを押さずに JavaScript を書ける Joke 言語
|
|
4
16
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[](README.md)
|
|
17
|
+
記号 (`!`, `"`, `(`, `)`, `{`, `}` …) を入力するときに Shift を押すのが面倒なので、Shift を押さずに JavaScript が書けるようにした Joke 言語です。
|
|
18
|
+
`^` プレフィックスのシーケンスで Shift が必要な記号を表現し、`.nsjs` ファイルは `nsc` CLI を使って JavaScript にコンパイルされます。
|
|
8
19
|
|
|
9
20
|
---
|
|
10
21
|
|
|
@@ -19,11 +30,11 @@ npm install -g noshift.js@latest
|
|
|
19
30
|
## はじめに
|
|
20
31
|
|
|
21
32
|
```bash
|
|
22
|
-
#
|
|
23
|
-
nsc create
|
|
33
|
+
# 新しいプロジェクトを作成
|
|
34
|
+
nsc create my-project
|
|
24
35
|
|
|
25
36
|
# または、現在のディレクトリに nsjsconfig.json だけを作成
|
|
26
|
-
nsc
|
|
37
|
+
nsc init
|
|
27
38
|
```
|
|
28
39
|
|
|
29
40
|
---
|
|
@@ -32,37 +43,41 @@ nsc --init
|
|
|
32
43
|
|
|
33
44
|
`nsc` は TypeScript の `tsc` に似た使い心地を目指しています。
|
|
34
45
|
|
|
35
|
-
| コマンド | 説明 |
|
|
36
|
-
|
|
37
|
-
| `nsc` | `nsjsconfig.json` を使って `.nsjs` → `.js` にコンパイル |
|
|
38
|
-
| `nsc -w`
|
|
39
|
-
| `nsc --init` | 現在のディレクトリに `nsjsconfig.json` を作成 |
|
|
40
|
-
| `nsc --clean` | 出力ディレクトリ (`
|
|
41
|
-
| `nsc run <file>` | `.nsjs` ファイルを直接実行 |
|
|
42
|
-
| `nsc create [name]` |
|
|
43
|
-
| `nsc -
|
|
44
|
-
| `nsc -h` | ヘルプを表示 |
|
|
46
|
+
| コマンド | エイリアス | 説明 |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `nsc` | | `nsjsconfig.json` を使って `.nsjs` → `.js` にコンパイル |
|
|
49
|
+
| `nsc watch` | `nsc -w` `nsc --watch` | 変更を監視して自動的に再コンパイル |
|
|
50
|
+
| `nsc init` | `nsc --init` | 現在のディレクトリに `nsjsconfig.json` を作成 |
|
|
51
|
+
| `nsc clean` | `nsc --clean` | 出力ディレクトリ (`outdir`) を削除 |
|
|
52
|
+
| `nsc run <file>` | `nsc -r <file>` `nsc --run <file>` | `.nsjs` ファイルを直接実行 |
|
|
53
|
+
| `nsc create [name]` | `nsc --create [name]` | 新しいプロジェクトを作成(`--no-prettier` で Prettier スキップ) |
|
|
54
|
+
| `nsc version` | `nsc -v` `nsc --version` | バージョンを表示 |
|
|
55
|
+
| `nsc help` | `nsc -h` `nsc --help` | ヘルプを表示 |
|
|
45
56
|
|
|
46
57
|
---
|
|
47
58
|
|
|
48
59
|
## nsjsconfig.json
|
|
49
60
|
|
|
50
61
|
プロジェクトルートに `nsjsconfig.json` を置くとコンパイル設定を行えます。
|
|
51
|
-
`nsc
|
|
62
|
+
`nsc init` または `nsc create` で自動生成されます。
|
|
52
63
|
|
|
53
64
|
```json
|
|
54
65
|
{
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
66
|
+
"compileroptions": {
|
|
67
|
+
"rootdir": "src",
|
|
68
|
+
"outdir": "dist",
|
|
69
|
+
"warnuppercase": true,
|
|
70
|
+
"capitalizeinstrings": true
|
|
58
71
|
}
|
|
59
72
|
}
|
|
60
73
|
```
|
|
61
74
|
|
|
62
75
|
| オプション | デフォルト | 説明 |
|
|
63
76
|
|---|---|---|
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
77
|
+
| `compileroptions.rootdir` | `"src"` | ソースディレクトリ |
|
|
78
|
+
| `compileroptions.outdir` | `"dist"` | 出力ディレクトリ |
|
|
79
|
+
| `compileroptions.warnuppercase` | `true` | ソースコード内の大文字を警告 |
|
|
80
|
+
| `compileroptions.capitalizeinstrings` | `true` | 文字列リテラル内で `^3` 大文字化修飾子を有効にする |
|
|
66
81
|
|
|
67
82
|
---
|
|
68
83
|
|
|
@@ -76,14 +91,14 @@ nsc --init
|
|
|
76
91
|
|:-------:|:--:|---|:-------:|:--:|
|
|
77
92
|
| `^1` | `!` | | `^^` | `~` |
|
|
78
93
|
| `^2` | `"` | | `^\` | `\|` |
|
|
79
|
-
| `^
|
|
80
|
-
| `^
|
|
81
|
-
| `^
|
|
82
|
-
| `^
|
|
83
|
-
| `^
|
|
84
|
-
| `^
|
|
85
|
-
|
|
|
86
|
-
|
|
|
94
|
+
| `^3x` | `X`(大文字化) | | `^@` | `` ` `` |
|
|
95
|
+
| `^4` | `$` | | `^[` | `{` |
|
|
96
|
+
| `^5` | `%` | | `^]` | `}` |
|
|
97
|
+
| `^6` | `&` | | `^;` | `+` |
|
|
98
|
+
| `^7` | `'` | | `^:` | `*` |
|
|
99
|
+
| `^8` | `(` | | `^,` | `<` |
|
|
100
|
+
| `^9` | `)` | | `^.` | `>` |
|
|
101
|
+
| `^-` | `=` | | `^/` | `?` |
|
|
87
102
|
|
|
88
103
|
テンプレート式: `^4^[` → `${`
|
|
89
104
|
|
|
@@ -101,6 +116,44 @@ console.log^8^2Hello, World!^2^9;
|
|
|
101
116
|
console.log("Hello, World!");
|
|
102
117
|
```
|
|
103
118
|
|
|
119
|
+
### 大文字化修飾子
|
|
120
|
+
|
|
121
|
+
`^3` は次の文字を大文字にします:
|
|
122
|
+
|
|
123
|
+
```nsjs
|
|
124
|
+
class ^3animal ^[
|
|
125
|
+
^]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
class Animal {
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### コメント
|
|
134
|
+
|
|
135
|
+
```nsjs
|
|
136
|
+
// 行コメント
|
|
137
|
+
|
|
138
|
+
/^: ブロックコメント ^:/
|
|
139
|
+
|
|
140
|
+
/^:
|
|
141
|
+
複数行の
|
|
142
|
+
ブロックコメント
|
|
143
|
+
^:/
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
// 行コメント
|
|
148
|
+
|
|
149
|
+
/* ブロックコメント */
|
|
150
|
+
|
|
151
|
+
/*
|
|
152
|
+
複数行の
|
|
153
|
+
ブロックコメント
|
|
154
|
+
*/
|
|
155
|
+
```
|
|
156
|
+
|
|
104
157
|
### 変数とアロー関数
|
|
105
158
|
|
|
106
159
|
```nsjs
|
|
@@ -165,7 +218,7 @@ const arr = [1, 2, 3];
|
|
|
165
218
|
### クラス
|
|
166
219
|
|
|
167
220
|
```nsjs
|
|
168
|
-
class
|
|
221
|
+
class ^3animal ^[
|
|
169
222
|
constructor^8name^9 ^[
|
|
170
223
|
this.name ^- name;
|
|
171
224
|
^]
|
|
@@ -175,7 +228,7 @@ class Animal ^[
|
|
|
175
228
|
^]
|
|
176
229
|
^]
|
|
177
230
|
|
|
178
|
-
const dog ^- new
|
|
231
|
+
const dog ^- new ^3animal^8^2Dog^2^9;
|
|
179
232
|
dog.speak^8^9;
|
|
180
233
|
```
|
|
181
234
|
|
package/README.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://www.npmjs.com/package/noshift.js) [](./LICENSE)
|
|
2
|
+
|
|
3
|
+
# NoShift.js
|
|
4
|
+
|
|
5
|
+
<div align="center">
|
|
6
|
+
<img src="https://raw.githubusercontent.com/otoneko1102/NoShift.js/refs/heads/main/icon.png" alt="noshift.js" width="128" height="128">
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div align="center">
|
|
10
|
+
|
|
11
|
+
**English** | [日本語](./README-ja.md)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
2
14
|
|
|
3
15
|
> A joke language that lets you write JavaScript without pressing the Shift key.
|
|
4
16
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
Typing shifted symbols (`!`, `"`, `(`, `)`, `{`, `}` …) is tiring.
|
|
18
|
+
**NoShift.js** replaces every shift-required symbol with a `^`-prefixed sequence, so you can write JavaScript using only unshifted keys.
|
|
19
|
+
`.nsjs` files compile directly to plain JavaScript via the `nsc` CLI.
|
|
8
20
|
|
|
9
21
|
---
|
|
10
22
|
|
|
@@ -19,11 +31,11 @@ npm install -g noshift.js@latest
|
|
|
19
31
|
## Getting Started
|
|
20
32
|
|
|
21
33
|
```bash
|
|
22
|
-
#
|
|
23
|
-
nsc create
|
|
34
|
+
# Create a new project
|
|
35
|
+
nsc create my-project
|
|
24
36
|
|
|
25
37
|
# Or initialize only a nsjsconfig.json in the current directory
|
|
26
|
-
nsc
|
|
38
|
+
nsc init
|
|
27
39
|
```
|
|
28
40
|
|
|
29
41
|
---
|
|
@@ -32,37 +44,41 @@ nsc --init
|
|
|
32
44
|
|
|
33
45
|
`nsc` is designed to feel like TypeScript's `tsc`.
|
|
34
46
|
|
|
35
|
-
| Command | Description |
|
|
36
|
-
|
|
37
|
-
| `nsc` | Compile `.nsjs` → `.js` using `nsjsconfig.json` |
|
|
38
|
-
| `nsc -w`
|
|
39
|
-
| `nsc --init` | Create `nsjsconfig.json` in the current directory |
|
|
40
|
-
| `nsc --clean` | Delete the output directory (`
|
|
41
|
-
| `nsc run <file>` | Run a `.nsjs` file directly |
|
|
42
|
-
| `nsc create [name]` | Scaffold a new project
|
|
43
|
-
| `nsc -
|
|
44
|
-
| `nsc -h` | Show help |
|
|
47
|
+
| Command | Alias | Description |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `nsc` | | Compile `.nsjs` → `.js` using `nsjsconfig.json` |
|
|
50
|
+
| `nsc watch` | `nsc -w` `nsc --watch` | Watch for changes and recompile automatically |
|
|
51
|
+
| `nsc init` | `nsc --init` | Create `nsjsconfig.json` in the current directory |
|
|
52
|
+
| `nsc clean` | `nsc --clean` | Delete the output directory (`outdir`) |
|
|
53
|
+
| `nsc run <file>` | `nsc -r <file>` `nsc --run <file>` | Run a `.nsjs` file directly |
|
|
54
|
+
| `nsc create [name]` | `nsc --create [name]` | Scaffold a new project (`--no-prettier` to skip Prettier) |
|
|
55
|
+
| `nsc version` | `nsc -v` `nsc --version` | Show version |
|
|
56
|
+
| `nsc help` | `nsc -h` `nsc --help` | Show help |
|
|
45
57
|
|
|
46
58
|
---
|
|
47
59
|
|
|
48
60
|
## nsjsconfig.json
|
|
49
61
|
|
|
50
62
|
Place a `nsjsconfig.json` at the project root to configure compilation.
|
|
51
|
-
Generated automatically by `nsc
|
|
63
|
+
Generated automatically by `nsc init` or `nsc create`.
|
|
52
64
|
|
|
53
65
|
```json
|
|
54
66
|
{
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
67
|
+
"compileroptions": {
|
|
68
|
+
"rootdir": "src",
|
|
69
|
+
"outdir": "dist",
|
|
70
|
+
"warnuppercase": true,
|
|
71
|
+
"capitalizeinstrings": true
|
|
58
72
|
}
|
|
59
73
|
}
|
|
60
74
|
```
|
|
61
75
|
|
|
62
76
|
| Option | Default | Description |
|
|
63
77
|
|---|---|---|
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
78
|
+
| `compileroptions.rootdir` | `"src"` | Source directory |
|
|
79
|
+
| `compileroptions.outdir` | `"dist"` | Output directory |
|
|
80
|
+
| `compileroptions.warnuppercase` | `true` | Warn about uppercase characters in source code |
|
|
81
|
+
| `compileroptions.capitalizeinstrings` | `true` | Enable `^3` capitalize modifier inside string literals |
|
|
66
82
|
|
|
67
83
|
---
|
|
68
84
|
|
|
@@ -76,14 +92,14 @@ Generated automatically by `nsc --init` or `nsc create`.
|
|
|
76
92
|
|:-------:|:--:|---|:-------:|:--:|
|
|
77
93
|
| `^1` | `!` | | `^^` | `~` |
|
|
78
94
|
| `^2` | `"` | | `^\` | `\|` |
|
|
79
|
-
| `^
|
|
80
|
-
| `^
|
|
81
|
-
| `^
|
|
82
|
-
| `^
|
|
83
|
-
| `^
|
|
84
|
-
| `^
|
|
85
|
-
|
|
|
86
|
-
|
|
|
95
|
+
| `^3x` | `X` (capitalize) | | `^@` | `` ` `` |
|
|
96
|
+
| `^4` | `$` | | `^[` | `{` |
|
|
97
|
+
| `^5` | `%` | | `^]` | `}` |
|
|
98
|
+
| `^6` | `&` | | `^;` | `+` |
|
|
99
|
+
| `^7` | `'` | | `^:` | `*` |
|
|
100
|
+
| `^8` | `(` | | `^,` | `<` |
|
|
101
|
+
| `^9` | `)` | | `^.` | `>` |
|
|
102
|
+
| `^-` | `=` | | `^/` | `?` |
|
|
87
103
|
|
|
88
104
|
Template expression: `^4^[` → `${`
|
|
89
105
|
|
|
@@ -101,6 +117,44 @@ console.log^8^2Hello, World!^2^9;
|
|
|
101
117
|
console.log("Hello, World!");
|
|
102
118
|
```
|
|
103
119
|
|
|
120
|
+
### Capitalize Modifier
|
|
121
|
+
|
|
122
|
+
`^3` capitalizes the next character:
|
|
123
|
+
|
|
124
|
+
```nsjs
|
|
125
|
+
class ^3animal ^[
|
|
126
|
+
^]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
class Animal {
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Comments
|
|
135
|
+
|
|
136
|
+
```nsjs
|
|
137
|
+
// line comment
|
|
138
|
+
|
|
139
|
+
/^: block comment ^:/
|
|
140
|
+
|
|
141
|
+
/^:
|
|
142
|
+
multi-line
|
|
143
|
+
block comment
|
|
144
|
+
^:/
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
// line comment
|
|
149
|
+
|
|
150
|
+
/* block comment */
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
multi-line
|
|
154
|
+
block comment
|
|
155
|
+
*/
|
|
156
|
+
```
|
|
157
|
+
|
|
104
158
|
### Variables & Arrow Functions
|
|
105
159
|
|
|
106
160
|
```nsjs
|
|
@@ -165,7 +219,7 @@ const arr = [1, 2, 3];
|
|
|
165
219
|
### Classes
|
|
166
220
|
|
|
167
221
|
```nsjs
|
|
168
|
-
class
|
|
222
|
+
class ^3animal ^[
|
|
169
223
|
constructor^8name^9 ^[
|
|
170
224
|
this.name ^- name;
|
|
171
225
|
^]
|
|
@@ -175,7 +229,7 @@ class Animal ^[
|
|
|
175
229
|
^]
|
|
176
230
|
^]
|
|
177
231
|
|
|
178
|
-
const dog ^- new
|
|
232
|
+
const dog ^- new ^3animal^8^2Dog^2^9;
|
|
179
233
|
dog.speak^8^9;
|
|
180
234
|
```
|
|
181
235
|
|
package/bin/cli.js
CHANGED
|
@@ -10,32 +10,52 @@ const pkg = JSON.parse(
|
|
|
10
10
|
readFileSync(path.join(__dirname, "../package.json"), "utf-8"),
|
|
11
11
|
);
|
|
12
12
|
|
|
13
|
+
const DOCS_URL = "https://noshift.js.org/";
|
|
14
|
+
|
|
13
15
|
const program = new Command();
|
|
14
16
|
|
|
15
|
-
// nsc [options] ← デフォルト動作: compile
|
|
16
17
|
program
|
|
17
18
|
.name("nsc")
|
|
18
19
|
.description("NoShift.js compiler")
|
|
19
|
-
.version(pkg.version,
|
|
20
|
+
.version(pkg.version, "-v, --version", "output the version number")
|
|
20
21
|
.option("-w, --watch", "Watch for file changes and recompile")
|
|
21
22
|
.option("--init", "Create a nsjsconfig.json in the current directory")
|
|
22
|
-
.option("--clean", "Delete the output directory (
|
|
23
|
+
.option("--clean", "Delete the output directory (outdir)")
|
|
24
|
+
.option("-r, --run <file>", "Run a .nsjs file directly")
|
|
25
|
+
.option("--create [name]", "Scaffold a new NoShift.js project")
|
|
26
|
+
.addHelpText("after", `\nDocumentation: ${DOCS_URL}`)
|
|
23
27
|
.action(async (options) => {
|
|
24
|
-
if (options.
|
|
28
|
+
if (options.watch) {
|
|
29
|
+
const { default: dev } = await import("../commands/dev.js");
|
|
30
|
+
await dev();
|
|
31
|
+
} else if (options.init) {
|
|
25
32
|
const { default: init } = await import("../commands/init.js");
|
|
26
33
|
await init();
|
|
27
34
|
} else if (options.clean) {
|
|
28
35
|
const { default: clean } = await import("../commands/clean.js");
|
|
29
36
|
await clean();
|
|
30
|
-
} else if (options.
|
|
31
|
-
const { default:
|
|
32
|
-
await
|
|
37
|
+
} else if (options.run) {
|
|
38
|
+
const { default: run } = await import("../commands/run.js");
|
|
39
|
+
await run(options.run);
|
|
40
|
+
} else if (options.create !== undefined) {
|
|
41
|
+
const { default: create } = await import("../commands/create.js");
|
|
42
|
+
await create(options.create || undefined);
|
|
33
43
|
} else {
|
|
34
44
|
const { default: compile } = await import("../commands/compile.js");
|
|
35
45
|
await compile();
|
|
36
46
|
}
|
|
37
47
|
});
|
|
38
48
|
|
|
49
|
+
// nsc watch
|
|
50
|
+
program
|
|
51
|
+
.command("watch")
|
|
52
|
+
.alias("w")
|
|
53
|
+
.description("Watch for file changes and recompile")
|
|
54
|
+
.action(async () => {
|
|
55
|
+
const { default: dev } = await import("../commands/dev.js");
|
|
56
|
+
await dev();
|
|
57
|
+
});
|
|
58
|
+
|
|
39
59
|
// nsc run <file>
|
|
40
60
|
program
|
|
41
61
|
.command("run <file>")
|
|
@@ -45,13 +65,49 @@ program
|
|
|
45
65
|
await run(file);
|
|
46
66
|
});
|
|
47
67
|
|
|
48
|
-
// nsc create [name]
|
|
68
|
+
// nsc create [name]
|
|
49
69
|
program
|
|
50
70
|
.command("create [name]")
|
|
51
|
-
.description("Scaffold a new NoShift.js project
|
|
52
|
-
.
|
|
71
|
+
.description("Scaffold a new NoShift.js project")
|
|
72
|
+
.option("--prettier", "Include Prettier (default)")
|
|
73
|
+
.option("--no-prettier", "Skip Prettier setup")
|
|
74
|
+
.action(async (name, options) => {
|
|
53
75
|
const { default: create } = await import("../commands/create.js");
|
|
54
|
-
await create(name);
|
|
76
|
+
await create(name, options);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// nsc init
|
|
80
|
+
program
|
|
81
|
+
.command("init")
|
|
82
|
+
.description("Create a nsjsconfig.json in the current directory")
|
|
83
|
+
.action(async () => {
|
|
84
|
+
const { default: init } = await import("../commands/init.js");
|
|
85
|
+
await init();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// nsc clean
|
|
89
|
+
program
|
|
90
|
+
.command("clean")
|
|
91
|
+
.description("Delete the output directory (outdir)")
|
|
92
|
+
.action(async () => {
|
|
93
|
+
const { default: clean } = await import("../commands/clean.js");
|
|
94
|
+
await clean();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// nsc version
|
|
98
|
+
program
|
|
99
|
+
.command("version")
|
|
100
|
+
.description("Display the current version")
|
|
101
|
+
.action(() => {
|
|
102
|
+
console.log(pkg.version);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// nsc help
|
|
106
|
+
program
|
|
107
|
+
.command("help")
|
|
108
|
+
.description("Show help information")
|
|
109
|
+
.action(() => {
|
|
110
|
+
program.outputHelp();
|
|
55
111
|
});
|
|
56
112
|
|
|
57
113
|
program.parse();
|
package/commands/clean.js
CHANGED
|
@@ -17,17 +17,17 @@ export default async function clean() {
|
|
|
17
17
|
process.exit(1);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const outDir = path.resolve(cwd, config.
|
|
20
|
+
const outDir = path.resolve(cwd, config.compileroptions.outdir);
|
|
21
21
|
|
|
22
22
|
try {
|
|
23
23
|
await access(outDir);
|
|
24
24
|
} catch {
|
|
25
25
|
logger.info(
|
|
26
|
-
`Nothing to clean (${logger.highlight(config.
|
|
26
|
+
`Nothing to clean (${logger.highlight(config.compileroptions.outdir)} does not exist).`,
|
|
27
27
|
);
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
await rm(outDir, { recursive: true, force: true });
|
|
32
|
-
logger.success(`Deleted ${logger.highlight(config.
|
|
32
|
+
logger.success(`Deleted ${logger.highlight(config.compileroptions.outdir)}`);
|
|
33
33
|
}
|
package/commands/compile.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import convert from "../src/convert.js";
|
|
3
|
+
import convert, { checkUppercaseWarnings } from "../src/convert.js";
|
|
4
4
|
import { loadConfig } from "../src/config.js";
|
|
5
5
|
import { handleSigint } from "../src/signal-handler.js";
|
|
6
6
|
import * as logger from "../src/logger.js";
|
|
@@ -39,15 +39,15 @@ export default async function compile() {
|
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const rootDir = path.resolve(cwd, config.
|
|
43
|
-
const outDir = path.resolve(cwd, config.
|
|
42
|
+
const rootDir = path.resolve(cwd, config.compileroptions.rootdir);
|
|
43
|
+
const outDir = path.resolve(cwd, config.compileroptions.outdir);
|
|
44
44
|
|
|
45
45
|
const files = await findNsjsFiles(rootDir);
|
|
46
46
|
|
|
47
47
|
if (files === null) {
|
|
48
48
|
logger.errorCode(
|
|
49
49
|
"NS0",
|
|
50
|
-
`
|
|
50
|
+
`rootdir '${config.compileroptions.rootdir}' not found.`,
|
|
51
51
|
);
|
|
52
52
|
process.exit(1);
|
|
53
53
|
}
|
|
@@ -61,6 +61,11 @@ export default async function compile() {
|
|
|
61
61
|
|
|
62
62
|
let compiled = 0;
|
|
63
63
|
let errors = 0;
|
|
64
|
+
let totalWarnings = 0;
|
|
65
|
+
const warnUppercase = config.compileroptions.warnuppercase !== false;
|
|
66
|
+
const convertOptions = {
|
|
67
|
+
capitalizeInStrings: config.compileroptions.capitalizeinstrings !== false,
|
|
68
|
+
};
|
|
64
69
|
|
|
65
70
|
for (const file of files) {
|
|
66
71
|
const relative = path.relative(rootDir, file);
|
|
@@ -68,7 +73,19 @@ export default async function compile() {
|
|
|
68
73
|
|
|
69
74
|
try {
|
|
70
75
|
const code = await fs.readFile(file, "utf-8");
|
|
71
|
-
|
|
76
|
+
|
|
77
|
+
// 大文字警告チェック
|
|
78
|
+
if (warnUppercase) {
|
|
79
|
+
const warnings = checkUppercaseWarnings(code);
|
|
80
|
+
for (const w of warnings) {
|
|
81
|
+
logger.warn(
|
|
82
|
+
`${relative.replace(/\\/g, "/")}:${w.line}:${w.column} - ${w.message}`,
|
|
83
|
+
);
|
|
84
|
+
totalWarnings++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const js = convert(code, convertOptions);
|
|
72
89
|
|
|
73
90
|
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
74
91
|
await fs.writeFile(destPath, js, "utf-8");
|
|
@@ -78,10 +95,7 @@ export default async function compile() {
|
|
|
78
95
|
);
|
|
79
96
|
compiled++;
|
|
80
97
|
} catch (e) {
|
|
81
|
-
logger.errorCode(
|
|
82
|
-
"NS1",
|
|
83
|
-
`${relative.replace(/\\/g, "/")}: ${e.message}`,
|
|
84
|
-
);
|
|
98
|
+
logger.errorCode("NS1", `${relative.replace(/\\/g, "/")}: ${e.message}`);
|
|
85
99
|
errors++;
|
|
86
100
|
}
|
|
87
101
|
}
|
|
@@ -91,6 +105,10 @@ export default async function compile() {
|
|
|
91
105
|
logger.error(`Found ${errors} error(s). Compiled ${compiled} file(s).`);
|
|
92
106
|
process.exit(1);
|
|
93
107
|
} else {
|
|
94
|
-
|
|
108
|
+
let msg = `Compiled ${compiled} file(s).`;
|
|
109
|
+
if (totalWarnings > 0) {
|
|
110
|
+
msg += ` (${totalWarnings} warning(s))`;
|
|
111
|
+
}
|
|
112
|
+
logger.success(msg);
|
|
95
113
|
}
|
|
96
114
|
}
|
package/commands/create.js
CHANGED
|
@@ -1,102 +1,84 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import
|
|
5
|
-
import { handleSigint, isUserCancelled } from "../src/signal-handler.js";
|
|
4
|
+
import { handleSigint } from "../src/signal-handler.js";
|
|
6
5
|
import * as logger from "../src/logger.js";
|
|
7
6
|
|
|
8
|
-
export default async function create(projectNameArg) {
|
|
7
|
+
export default async function create(projectNameArg, options = {}) {
|
|
9
8
|
handleSigint();
|
|
10
9
|
|
|
11
10
|
const cwd = process.cwd();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
await fs.writeFile(
|
|
57
|
-
"nsjsconfig.json",
|
|
58
|
-
JSON.stringify(nsjsconfig, null, 2) + "\n",
|
|
59
|
-
);
|
|
60
|
-
logger.success("Created nsjsconfig.json");
|
|
61
|
-
|
|
62
|
-
// Prettier
|
|
63
|
-
const { usePrettier } = await inquirer.prompt([
|
|
64
|
-
{
|
|
65
|
-
type: "confirm",
|
|
66
|
-
name: "usePrettier",
|
|
67
|
-
message: "Format compiled output with Prettier?",
|
|
68
|
-
default: true,
|
|
69
|
-
},
|
|
70
|
-
]);
|
|
71
|
-
|
|
72
|
-
if (usePrettier) {
|
|
73
|
-
logger.step("Installing Prettier...");
|
|
74
|
-
execSync("npm install --save-dev prettier", { stdio: "ignore" });
|
|
75
|
-
await fs.writeFile(".prettierignore", "dist/\nnode_modules/\n");
|
|
76
|
-
await fs.writeFile(
|
|
77
|
-
".prettierrc",
|
|
78
|
-
JSON.stringify(
|
|
79
|
-
{ semi: true, singleQuote: false, trailingComma: "es5" },
|
|
80
|
-
null,
|
|
81
|
-
2,
|
|
82
|
-
) + "\n",
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Install noshift.js
|
|
87
|
-
logger.step("Installing noshift.js...");
|
|
88
|
-
execSync("npm install noshift.js", { stdio: "ignore" });
|
|
89
|
-
|
|
90
|
-
// Create project files
|
|
91
|
-
logger.step("Creating project files...");
|
|
92
|
-
await fs.mkdir("src", { recursive: true });
|
|
11
|
+
const projectName = projectNameArg || "my-noshift-app";
|
|
12
|
+
const usePrettier = options.prettier !== false; // default: true
|
|
13
|
+
|
|
14
|
+
const projectPath = path.join(cwd, projectName);
|
|
15
|
+
|
|
16
|
+
// Create project directory
|
|
17
|
+
logger.step("Creating project directory...");
|
|
18
|
+
await fs.mkdir(projectPath, { recursive: true });
|
|
19
|
+
logger.dim(` ${projectPath}`);
|
|
20
|
+
|
|
21
|
+
process.chdir(projectPath);
|
|
22
|
+
|
|
23
|
+
// npm init
|
|
24
|
+
logger.step("Initializing npm...");
|
|
25
|
+
execSync("npm init -y", { stdio: "ignore" });
|
|
26
|
+
|
|
27
|
+
// Add scripts to package.json
|
|
28
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
29
|
+
const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
|
|
30
|
+
pkg.scripts = pkg.scripts ?? {};
|
|
31
|
+
pkg.scripts.compile = "nsc compile";
|
|
32
|
+
pkg.scripts.dev = "nsc dev";
|
|
33
|
+
await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
34
|
+
|
|
35
|
+
// Create nsjsconfig.json
|
|
36
|
+
const nsjsconfig = {
|
|
37
|
+
compileroptions: {
|
|
38
|
+
rootdir: "src",
|
|
39
|
+
outdir: "dist",
|
|
40
|
+
warnuppercase: true,
|
|
41
|
+
capitalizeinstrings: true,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
await fs.writeFile(
|
|
45
|
+
"nsjsconfig.json",
|
|
46
|
+
JSON.stringify(nsjsconfig, null, 2) + "\n",
|
|
47
|
+
);
|
|
48
|
+
logger.success("Created nsjsconfig.json");
|
|
49
|
+
|
|
50
|
+
// Prettier
|
|
51
|
+
if (usePrettier) {
|
|
52
|
+
logger.step("Installing Prettier...");
|
|
53
|
+
execSync("npm install --save-dev prettier", { stdio: "ignore" });
|
|
54
|
+
await fs.writeFile(".prettierignore", "dist/\nnode_modules/\n");
|
|
93
55
|
await fs.writeFile(
|
|
94
|
-
"
|
|
95
|
-
|
|
56
|
+
".prettierrc",
|
|
57
|
+
JSON.stringify(
|
|
58
|
+
{ semi: true, singleQuote: false, trailingComma: "es5" },
|
|
59
|
+
null,
|
|
60
|
+
2,
|
|
61
|
+
) + "\n",
|
|
96
62
|
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Install noshift.js
|
|
66
|
+
logger.step("Installing noshift.js...");
|
|
67
|
+
execSync("npm install noshift.js", { stdio: "ignore" });
|
|
97
68
|
|
|
98
|
-
|
|
99
|
-
|
|
69
|
+
// Create project files
|
|
70
|
+
logger.step("Creating project files...");
|
|
71
|
+
await fs.mkdir("src", { recursive: true });
|
|
72
|
+
await fs.writeFile(
|
|
73
|
+
"src/index.nsjs",
|
|
74
|
+
"console.log^8^2^3hello, ^3world!^2^9;\n",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// .gitignore
|
|
78
|
+
await fs.writeFile(".gitignore", "node_modules/\ndist/\n");
|
|
79
|
+
|
|
80
|
+
// README.md
|
|
81
|
+
const readme = `# ${projectName}
|
|
100
82
|
|
|
101
83
|
A [NoShift.js](https://github.com/otoneko1102/NoShift.js) project.
|
|
102
84
|
|
|
@@ -113,20 +95,14 @@ npm run dev
|
|
|
113
95
|
\`\`\`
|
|
114
96
|
`;
|
|
115
97
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
} catch (error) {
|
|
127
|
-
if (isUserCancelled(error)) {
|
|
128
|
-
process.exit(0);
|
|
129
|
-
}
|
|
130
|
-
throw error;
|
|
131
|
-
}
|
|
98
|
+
await fs.writeFile("README.md", readme);
|
|
99
|
+
|
|
100
|
+
// Success message
|
|
101
|
+
console.log("");
|
|
102
|
+
logger.success("Project created successfully!");
|
|
103
|
+
console.log("");
|
|
104
|
+
logger.info("Next steps:");
|
|
105
|
+
console.log(` ${logger.highlight(`cd ${projectName}`)}`);
|
|
106
|
+
console.log(` ${logger.highlight("npm run compile")}`);
|
|
107
|
+
console.log("");
|
|
132
108
|
}
|
package/commands/dev.js
CHANGED
|
@@ -29,13 +29,13 @@ async function findNsjsFiles(dir) {
|
|
|
29
29
|
return files;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async function compileFile(file, rootDir, outDir, cwd) {
|
|
32
|
+
async function compileFile(file, rootDir, outDir, cwd, convertOptions = {}) {
|
|
33
33
|
const relative = path.relative(rootDir, file).replace(/\\/g, "/");
|
|
34
34
|
const destPath = path
|
|
35
35
|
.join(outDir, path.relative(rootDir, file))
|
|
36
36
|
.replace(/\.nsjs$/, ".js");
|
|
37
37
|
const code = await fs.readFile(file, "utf-8");
|
|
38
|
-
const js = convert(code);
|
|
38
|
+
const js = convert(code, convertOptions);
|
|
39
39
|
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
40
40
|
await fs.writeFile(destPath, js, "utf-8");
|
|
41
41
|
logger.dim(
|
|
@@ -54,15 +54,18 @@ export default async function dev() {
|
|
|
54
54
|
process.exit(1);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const rootDir = path.resolve(cwd, config.
|
|
58
|
-
const outDir = path.resolve(cwd, config.
|
|
57
|
+
const rootDir = path.resolve(cwd, config.compileroptions.rootdir);
|
|
58
|
+
const outDir = path.resolve(cwd, config.compileroptions.outdir);
|
|
59
|
+
const convertOptions = {
|
|
60
|
+
capitalizeInStrings: config.compileroptions.capitalizeinstrings !== false,
|
|
61
|
+
};
|
|
59
62
|
|
|
60
63
|
// 初回フルコンパイル
|
|
61
64
|
const files = await findNsjsFiles(rootDir);
|
|
62
65
|
if (files === null) {
|
|
63
66
|
logger.errorCode(
|
|
64
67
|
"NS0",
|
|
65
|
-
`
|
|
68
|
+
`rootdir '${config.compileroptions.rootdir}' not found.`,
|
|
66
69
|
);
|
|
67
70
|
process.exit(1);
|
|
68
71
|
}
|
|
@@ -73,7 +76,7 @@ export default async function dev() {
|
|
|
73
76
|
|
|
74
77
|
for (const file of files) {
|
|
75
78
|
try {
|
|
76
|
-
await compileFile(file, rootDir, outDir, cwd);
|
|
79
|
+
await compileFile(file, rootDir, outDir, cwd, convertOptions);
|
|
77
80
|
} catch (e) {
|
|
78
81
|
const rel = path.relative(rootDir, file).replace(/\\/g, "/");
|
|
79
82
|
logger.errorCode("NS1", `${rel}: ${e.message}`);
|
|
@@ -81,7 +84,7 @@ export default async function dev() {
|
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
logger.info(
|
|
84
|
-
`Watching for file changes in '${logger.highlight(config.
|
|
87
|
+
`Watching for file changes in '${logger.highlight(config.compileroptions.rootdir)}'... (Press Ctrl+C to stop)`,
|
|
85
88
|
);
|
|
86
89
|
console.log("");
|
|
87
90
|
|
|
@@ -109,7 +112,7 @@ export default async function dev() {
|
|
|
109
112
|
debounceMap.delete(filename);
|
|
110
113
|
const absPath = path.join(rootDir, filename);
|
|
111
114
|
try {
|
|
112
|
-
await compileFile(absPath, rootDir, outDir, cwd);
|
|
115
|
+
await compileFile(absPath, rootDir, outDir, cwd, convertOptions);
|
|
113
116
|
} catch (e) {
|
|
114
117
|
if (e.code === "ENOENT") {
|
|
115
118
|
// ファイルが削除された場合はスキップ
|
package/commands/init.js
CHANGED
|
@@ -4,9 +4,11 @@ import { handleSigint } from "../src/signal-handler.js";
|
|
|
4
4
|
import * as logger from "../src/logger.js";
|
|
5
5
|
|
|
6
6
|
const DEFAULT_CONFIG = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
compileroptions: {
|
|
8
|
+
rootdir: "src",
|
|
9
|
+
outdir: "dist",
|
|
10
|
+
warnuppercase: true,
|
|
11
|
+
capitalizeinstrings: true,
|
|
10
12
|
},
|
|
11
13
|
};
|
|
12
14
|
|
|
@@ -28,7 +30,11 @@ export default async function init() {
|
|
|
28
30
|
|
|
29
31
|
await writeFile(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n");
|
|
30
32
|
logger.success("Created nsjsconfig.json");
|
|
31
|
-
logger.dim(
|
|
32
|
-
|
|
33
|
+
logger.dim(
|
|
34
|
+
` compileroptions.rootdir : ${DEFAULT_CONFIG.compileroptions.rootdir}`,
|
|
35
|
+
);
|
|
36
|
+
logger.dim(
|
|
37
|
+
` compileroptions.outdir : ${DEFAULT_CONFIG.compileroptions.outdir}`,
|
|
38
|
+
);
|
|
33
39
|
console.log("");
|
|
34
40
|
}
|
package/commands/run.js
CHANGED
|
@@ -2,13 +2,26 @@ import { promises as fs } from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { spawn } from "child_process";
|
|
4
4
|
import convert from "../src/convert.js";
|
|
5
|
+
import { loadConfig } from "../src/config.js";
|
|
5
6
|
import { handleSigint } from "../src/signal-handler.js";
|
|
6
7
|
import * as logger from "../src/logger.js";
|
|
7
8
|
|
|
8
9
|
export default async function run(file) {
|
|
9
10
|
handleSigint();
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
+
const cwd = process.cwd();
|
|
13
|
+
let config;
|
|
14
|
+
try {
|
|
15
|
+
config = await loadConfig(cwd);
|
|
16
|
+
} catch {
|
|
17
|
+
config = { compileroptions: {} };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const convertOptions = {
|
|
21
|
+
capitalizeInStrings: config.compileroptions.capitalizeinstrings !== false,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const filePath = path.resolve(cwd, file);
|
|
12
25
|
|
|
13
26
|
let code;
|
|
14
27
|
try {
|
|
@@ -18,7 +31,7 @@ export default async function run(file) {
|
|
|
18
31
|
process.exit(1);
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
const js = convert(code);
|
|
34
|
+
const js = convert(code, convertOptions);
|
|
22
35
|
|
|
23
36
|
// ソースファイルと同じディレクトリに一時ファイルを作成する。
|
|
24
37
|
// これにより、コンパイル後コード内の相対 import が正しく解決される。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noshift.js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Joke language.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"nsc": "./bin/cli.js"
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://otoneko1102.github.io/NoShift.js/",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"commander": "^14.0.3"
|
|
31
|
-
"inquirer": "^13.2.2"
|
|
30
|
+
"commander": "^14.0.3"
|
|
32
31
|
},
|
|
33
32
|
"devDependencies": {
|
|
34
33
|
"prettier": "^3.8.1"
|
package/src/config.js
CHANGED
|
@@ -2,9 +2,11 @@ import { promises as fs } from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
|
|
4
4
|
const DEFAULT_CONFIG = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
compileroptions: {
|
|
6
|
+
rootdir: "src",
|
|
7
|
+
outdir: "dist",
|
|
8
|
+
warnuppercase: true,
|
|
9
|
+
capitalizeinstrings: true,
|
|
8
10
|
},
|
|
9
11
|
};
|
|
10
12
|
|
|
@@ -20,9 +22,9 @@ export async function loadConfig(cwd = process.cwd()) {
|
|
|
20
22
|
const userConfig = JSON.parse(raw);
|
|
21
23
|
|
|
22
24
|
return {
|
|
23
|
-
|
|
24
|
-
...DEFAULT_CONFIG.
|
|
25
|
-
...(userConfig.
|
|
25
|
+
compileroptions: {
|
|
26
|
+
...DEFAULT_CONFIG.compileroptions,
|
|
27
|
+
...(userConfig.compileroptions ?? {}),
|
|
26
28
|
},
|
|
27
29
|
};
|
|
28
30
|
} catch (e) {
|
package/src/convert.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
function convertNsjsToJs(nsjsCode) {
|
|
1
|
+
function convertNsjsToJs(nsjsCode, options = {}) {
|
|
2
|
+
const capitalizeInStrings = options.capitalizeInStrings !== false;
|
|
2
3
|
let jsCode = "";
|
|
3
4
|
let i = 0;
|
|
4
5
|
const len_ns = nsjsCode.length;
|
|
@@ -15,6 +16,8 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
15
16
|
IN_TEMPLATE_EXPRESSION: "IN_TEMPLATE_EXPRESSION", // ${ … } の中
|
|
16
17
|
RAW_DQ_IN_EXPR: "RAW_DQ_IN_EXPR", // テンプレート式内の " … " の中 (NoShift 変換なし)
|
|
17
18
|
RAW_SQ_IN_EXPR: "RAW_SQ_IN_EXPR", // テンプレート式内の ' … ' の中 (NoShift 変換なし)
|
|
19
|
+
IN_LINE_COMMENT: "IN_LINE_COMMENT", // // … の中
|
|
20
|
+
IN_BLOCK_COMMENT: "IN_BLOCK_COMMENT", // /^: … ^:/ の中
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
let currentState = STATE.NORMAL;
|
|
@@ -236,7 +239,11 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
236
239
|
|
|
237
240
|
// (A) IN_DQ_STRING 内 (" … ")
|
|
238
241
|
if (currentState === STATE.IN_DQ_STRING) {
|
|
239
|
-
if (nsjsCode.startsWith("\\^
|
|
242
|
+
if (nsjsCode.startsWith("\\^3", i)) {
|
|
243
|
+
jsCode += "^3"; // "\^3" を文字列中のリテラル "^3" として出力
|
|
244
|
+
i += 3;
|
|
245
|
+
consumed = true;
|
|
246
|
+
} else if (nsjsCode.startsWith("\\^2", i)) {
|
|
240
247
|
jsCode += "^2"; // "\^2" を文字列中の "^2" として出力
|
|
241
248
|
i += 3;
|
|
242
249
|
consumed = true;
|
|
@@ -248,7 +255,11 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
248
255
|
}
|
|
249
256
|
// (B) IN_SQ_STRING 内 (' … ')
|
|
250
257
|
else if (currentState === STATE.IN_SQ_STRING) {
|
|
251
|
-
if (nsjsCode.startsWith("\\^
|
|
258
|
+
if (nsjsCode.startsWith("\\^3", i)) {
|
|
259
|
+
jsCode += "^3";
|
|
260
|
+
i += 3;
|
|
261
|
+
consumed = true;
|
|
262
|
+
} else if (nsjsCode.startsWith("\\^7", i)) {
|
|
252
263
|
jsCode += "^7";
|
|
253
264
|
i += 3;
|
|
254
265
|
consumed = true;
|
|
@@ -260,7 +271,11 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
260
271
|
}
|
|
261
272
|
// (C) RAW_DQ_IN_EXPR 内 (テンプレート式中の " … ")
|
|
262
273
|
else if (currentState === STATE.RAW_DQ_IN_EXPR) {
|
|
263
|
-
if (nsjsCode.startsWith("\\^
|
|
274
|
+
if (nsjsCode.startsWith("\\^3", i)) {
|
|
275
|
+
jsCode += "^3";
|
|
276
|
+
i += 3;
|
|
277
|
+
consumed = true;
|
|
278
|
+
} else if (nsjsCode.startsWith("\\^2", i)) {
|
|
264
279
|
jsCode += "^2";
|
|
265
280
|
i += 3;
|
|
266
281
|
consumed = true;
|
|
@@ -284,7 +299,11 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
284
299
|
}
|
|
285
300
|
// (D) RAW_SQ_IN_EXPR 内 (テンプレート式中の ' … ')
|
|
286
301
|
else if (currentState === STATE.RAW_SQ_IN_EXPR) {
|
|
287
|
-
if (nsjsCode.startsWith("\\^
|
|
302
|
+
if (nsjsCode.startsWith("\\^3", i)) {
|
|
303
|
+
jsCode += "^3";
|
|
304
|
+
i += 3;
|
|
305
|
+
consumed = true;
|
|
306
|
+
} else if (nsjsCode.startsWith("\\^7", i)) {
|
|
288
307
|
jsCode += "^7";
|
|
289
308
|
i += 3;
|
|
290
309
|
consumed = true;
|
|
@@ -308,7 +327,11 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
308
327
|
}
|
|
309
328
|
// (E) IN_BT_SINGLE_STRING 内 (` … `)
|
|
310
329
|
else if (currentState === STATE.IN_BT_SINGLE_STRING) {
|
|
311
|
-
if (nsjsCode.startsWith("
|
|
330
|
+
if (nsjsCode.startsWith("\\^3", i)) {
|
|
331
|
+
jsCode += "^3";
|
|
332
|
+
i += 3;
|
|
333
|
+
consumed = true;
|
|
334
|
+
} else if (nsjsCode.startsWith("\\^@", i)) {
|
|
312
335
|
jsCode += "^@";
|
|
313
336
|
i += 3;
|
|
314
337
|
consumed = true;
|
|
@@ -333,14 +356,93 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
333
356
|
}
|
|
334
357
|
|
|
335
358
|
// ======
|
|
336
|
-
// ステップ2:
|
|
359
|
+
// ステップ2: ^3 大文字化モディファイア (RAW 状態とコメント以外で動作)
|
|
360
|
+
// - コード上 (NORMAL, IN_TEMPLATE_EXPRESSION) では常に有効
|
|
361
|
+
// - 文字列内は capitalizeInStrings オプションに従う
|
|
362
|
+
// ======
|
|
363
|
+
if (
|
|
364
|
+
!consumed &&
|
|
365
|
+
currentState !== STATE.RAW_DQ_IN_EXPR &&
|
|
366
|
+
currentState !== STATE.RAW_SQ_IN_EXPR &&
|
|
367
|
+
currentState !== STATE.IN_LINE_COMMENT &&
|
|
368
|
+
currentState !== STATE.IN_BLOCK_COMMENT
|
|
369
|
+
) {
|
|
370
|
+
if (nsjsCode.startsWith("^3", i)) {
|
|
371
|
+
const inString =
|
|
372
|
+
currentState === STATE.IN_DQ_STRING ||
|
|
373
|
+
currentState === STATE.IN_SQ_STRING ||
|
|
374
|
+
currentState === STATE.IN_BT_SINGLE_STRING ||
|
|
375
|
+
currentState === STATE.IN_BT_MULTI_STRING;
|
|
376
|
+
if (!inString || capitalizeInStrings) {
|
|
377
|
+
i += 2;
|
|
378
|
+
if (i < len_ns) {
|
|
379
|
+
jsCode += nsjsCode[i].toUpperCase();
|
|
380
|
+
i += 1;
|
|
381
|
+
}
|
|
382
|
+
consumed = true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ======
|
|
388
|
+
// ステップ2.5: コメントの処理
|
|
389
|
+
// ======
|
|
390
|
+
if (!consumed) {
|
|
391
|
+
// 行コメント開始 (//)
|
|
392
|
+
if (currentState === STATE.NORMAL && nsjsCode.startsWith("//", i)) {
|
|
393
|
+
jsCode += "//";
|
|
394
|
+
i += 2;
|
|
395
|
+
stateStack.push(currentState);
|
|
396
|
+
currentState = STATE.IN_LINE_COMMENT;
|
|
397
|
+
consumed = true;
|
|
398
|
+
}
|
|
399
|
+
// 行コメント終了 (改行)
|
|
400
|
+
else if (currentState === STATE.IN_LINE_COMMENT) {
|
|
401
|
+
if (nsjsCode[i] === "\n") {
|
|
402
|
+
jsCode += "\n";
|
|
403
|
+
i += 1;
|
|
404
|
+
currentState = stateStack.pop();
|
|
405
|
+
} else {
|
|
406
|
+
jsCode += nsjsCode[i];
|
|
407
|
+
i += 1;
|
|
408
|
+
}
|
|
409
|
+
consumed = true;
|
|
410
|
+
}
|
|
411
|
+
// ブロックコメント開始 (/^:)
|
|
412
|
+
else if (currentState === STATE.NORMAL && nsjsCode.startsWith("/^:", i)) {
|
|
413
|
+
jsCode += "/*";
|
|
414
|
+
i += 3;
|
|
415
|
+
stateStack.push(currentState);
|
|
416
|
+
currentState = STATE.IN_BLOCK_COMMENT;
|
|
417
|
+
consumed = true;
|
|
418
|
+
}
|
|
419
|
+
// ブロックコメント終了 (^:/)
|
|
420
|
+
else if (
|
|
421
|
+
currentState === STATE.IN_BLOCK_COMMENT &&
|
|
422
|
+
nsjsCode.startsWith("^:/", i)
|
|
423
|
+
) {
|
|
424
|
+
jsCode += "*/";
|
|
425
|
+
i += 3;
|
|
426
|
+
currentState = stateStack.pop();
|
|
427
|
+
consumed = true;
|
|
428
|
+
}
|
|
429
|
+
// ブロックコメント内の文字 (そのまま出力)
|
|
430
|
+
else if (currentState === STATE.IN_BLOCK_COMMENT) {
|
|
431
|
+
jsCode += nsjsCode[i];
|
|
432
|
+
i += 1;
|
|
433
|
+
consumed = true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ======
|
|
438
|
+
// ステップ3: NoShift シーケンスや文字列/テンプレートの開閉、式展開を試す
|
|
337
439
|
// ======
|
|
338
440
|
if (!consumed) {
|
|
339
441
|
consumed = tryConsumeNsjsSequence();
|
|
340
442
|
}
|
|
341
443
|
|
|
342
444
|
// ======
|
|
343
|
-
// ステップ
|
|
445
|
+
// ステップ4: 何も消費しなかったら文字をそのまま出力
|
|
344
446
|
// ======
|
|
345
447
|
if (!consumed) {
|
|
346
448
|
jsCode += nsjsCode[i];
|
|
@@ -359,4 +461,139 @@ function convertNsjsToJs(nsjsCode) {
|
|
|
359
461
|
return jsCode;
|
|
360
462
|
}
|
|
361
463
|
|
|
464
|
+
/**
|
|
465
|
+
* ソースコード内の大文字、_, $, # の使用を警告する。
|
|
466
|
+
* 文字列・コメント内は無視する。
|
|
467
|
+
* @param {string} nsjsCode
|
|
468
|
+
* @returns {{ line: number, column: number, char: string, message: string }[]}
|
|
469
|
+
*/
|
|
470
|
+
export function checkUppercaseWarnings(nsjsCode) {
|
|
471
|
+
const warnings = [];
|
|
472
|
+
const lines = nsjsCode.split("\n");
|
|
473
|
+
|
|
474
|
+
// シンプルな状態追跡(文字列・コメント内を除外)
|
|
475
|
+
let inDQ = false; // ^2...^2
|
|
476
|
+
let inSQ = false; // ^7...^7
|
|
477
|
+
let inBT = false; // ^@...^@
|
|
478
|
+
let inLineComment = false;
|
|
479
|
+
let inBlockComment = false;
|
|
480
|
+
|
|
481
|
+
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
482
|
+
const line = lines[lineNum];
|
|
483
|
+
inLineComment = false; // 行コメントは行ごとにリセット
|
|
484
|
+
|
|
485
|
+
for (let col = 0; col < line.length; col++) {
|
|
486
|
+
const ch = line[col];
|
|
487
|
+
const next = line[col + 1];
|
|
488
|
+
|
|
489
|
+
// エスケープ (\^2, \^7, \^@) をスキップ
|
|
490
|
+
if (ch === "\\" && next === "^") {
|
|
491
|
+
col += 2;
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// ブロックコメント終了 (^:/)
|
|
496
|
+
if (
|
|
497
|
+
inBlockComment &&
|
|
498
|
+
ch === "^" &&
|
|
499
|
+
next === ":" &&
|
|
500
|
+
line[col + 2] === "/"
|
|
501
|
+
) {
|
|
502
|
+
inBlockComment = false;
|
|
503
|
+
col += 2;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (inBlockComment) continue;
|
|
507
|
+
|
|
508
|
+
// 行コメント開始
|
|
509
|
+
if (!inDQ && !inSQ && !inBT && ch === "/" && next === "/") {
|
|
510
|
+
inLineComment = true;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
// ブロックコメント開始 (/^:)
|
|
514
|
+
if (
|
|
515
|
+
!inDQ &&
|
|
516
|
+
!inSQ &&
|
|
517
|
+
!inBT &&
|
|
518
|
+
ch === "/" &&
|
|
519
|
+
next === "^" &&
|
|
520
|
+
line[col + 2] === ":"
|
|
521
|
+
) {
|
|
522
|
+
inBlockComment = true;
|
|
523
|
+
col += 2;
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (inLineComment) continue;
|
|
528
|
+
|
|
529
|
+
// ^3 モディファイア → 次の文字はスキップ(意図的な大文字化)
|
|
530
|
+
if (ch === "^" && next === "3") {
|
|
531
|
+
col += 2; // ^3 と次の文字をスキップ
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// 文字列リテラルの開閉
|
|
536
|
+
if (ch === "^" && next === "2") {
|
|
537
|
+
inDQ = !inDQ;
|
|
538
|
+
col += 1;
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (ch === "^" && next === "7") {
|
|
542
|
+
inSQ = !inSQ;
|
|
543
|
+
col += 1;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (ch === "^" && next === "@") {
|
|
547
|
+
inBT = !inBT;
|
|
548
|
+
col += 1;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// 文字列内はスキップ
|
|
553
|
+
if (inDQ || inSQ || inBT) continue;
|
|
554
|
+
|
|
555
|
+
// 他の ^X シーケンスをスキップ
|
|
556
|
+
if (ch === "^" && next && /[0-9\-^\\@\[\];:,./]/.test(next)) {
|
|
557
|
+
col += 1;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// 大文字の警告
|
|
562
|
+
if (/[A-Z]/.test(ch)) {
|
|
563
|
+
warnings.push({
|
|
564
|
+
line: lineNum + 1,
|
|
565
|
+
column: col + 1,
|
|
566
|
+
char: ch,
|
|
567
|
+
message: `Uppercase letter '${ch}' found. Use ^3${ch.toLowerCase()} instead.`,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
// _, $, # の警告
|
|
571
|
+
else if (ch === "_") {
|
|
572
|
+
warnings.push({
|
|
573
|
+
line: lineNum + 1,
|
|
574
|
+
column: col + 1,
|
|
575
|
+
char: ch,
|
|
576
|
+
message: "Underscore '_' found in code.",
|
|
577
|
+
});
|
|
578
|
+
} else if (ch === "$") {
|
|
579
|
+
warnings.push({
|
|
580
|
+
line: lineNum + 1,
|
|
581
|
+
column: col + 1,
|
|
582
|
+
char: ch,
|
|
583
|
+
message: "Dollar sign '$' found. Use ^4 instead.",
|
|
584
|
+
});
|
|
585
|
+
} else if (ch === "#") {
|
|
586
|
+
warnings.push({
|
|
587
|
+
line: lineNum + 1,
|
|
588
|
+
column: col + 1,
|
|
589
|
+
char: ch,
|
|
590
|
+
message: "Hash '#' found in code.",
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return warnings;
|
|
597
|
+
}
|
|
598
|
+
|
|
362
599
|
export default convertNsjsToJs;
|
package/src/logger.js
CHANGED