noshift.js 0.2.1 → 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 +9 -5
- package/commands/compile.js +39 -14
- package/commands/create.js +46 -111
- package/commands/dev.js +29 -16
- package/commands/init.js +20 -8
- package/commands/run.js +20 -3
- package/package.json +2 -3
- package/src/config.js +8 -6
- package/src/convert.js +245 -8
- package/src/logger.js +50 -0
- package/src/signal-handler.js +46 -0
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
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// シンプルなロガーユーティリティ(ANSI エスケープコードを使用)
|
|
2
|
+
|
|
3
|
+
const colors = {
|
|
4
|
+
reset: "\x1b[0m",
|
|
5
|
+
bright: "\x1b[1m",
|
|
6
|
+
dim: "\x1b[2m",
|
|
7
|
+
red: "\x1b[31m",
|
|
8
|
+
green: "\x1b[32m",
|
|
9
|
+
yellow: "\x1b[33m",
|
|
10
|
+
blue: "\x1b[34m",
|
|
11
|
+
magenta: "\x1b[35m",
|
|
12
|
+
cyan: "\x1b[36m",
|
|
13
|
+
gray: "\x1b[90m",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function success(message) {
|
|
17
|
+
console.log(`${colors.green}✓${colors.reset} ${message}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function error(message) {
|
|
21
|
+
console.error(`${colors.red}✗${colors.reset} ${message}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function info(message) {
|
|
25
|
+
console.log(`${colors.blue}ℹ${colors.reset} ${message}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function warn(message) {
|
|
29
|
+
console.log(`${colors.yellow}⚠${colors.reset} ${message}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function step(message) {
|
|
33
|
+
console.log(`${colors.cyan}→${colors.reset} ${message}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function dim(message) {
|
|
37
|
+
console.log(`${colors.dim}${message}${colors.reset}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function bold(text) {
|
|
41
|
+
return `${colors.bright}${text}${colors.reset}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function highlight(text) {
|
|
45
|
+
return `${colors.cyan}${text}${colors.reset}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function errorCode(code, message) {
|
|
49
|
+
console.error(`${colors.red}error ${code}:${colors.reset} ${message}`);
|
|
50
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// シグナルハンドリングユーティリティ
|
|
2
|
+
|
|
3
|
+
let isHandlerRegistered = false;
|
|
4
|
+
let cleanupCallbacks = [];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ctrl+C (SIGINT) を適切にハンドリングする
|
|
8
|
+
* @param {Function} cleanup - クリーンアップ時に実行する関数(オプション)
|
|
9
|
+
*/
|
|
10
|
+
export function handleSigint(cleanup) {
|
|
11
|
+
if (cleanup) {
|
|
12
|
+
cleanupCallbacks.push(cleanup);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!isHandlerRegistered) {
|
|
16
|
+
isHandlerRegistered = true;
|
|
17
|
+
|
|
18
|
+
process.on("SIGINT", async () => {
|
|
19
|
+
console.log("\n"); // 改行を追加してきれいに終了
|
|
20
|
+
|
|
21
|
+
// すべてのクリーンアップコールバックを実行
|
|
22
|
+
for (const cb of cleanupCallbacks) {
|
|
23
|
+
try {
|
|
24
|
+
await cb();
|
|
25
|
+
} catch (e) {
|
|
26
|
+
// エラーは無視
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* inquirer のキャンセルエラーをチェック
|
|
37
|
+
* @param {Error} error
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
export function isUserCancelled(error) {
|
|
41
|
+
return (
|
|
42
|
+
error &&
|
|
43
|
+
(error.name === "ExitPromptError" ||
|
|
44
|
+
error.message === "User force closed the prompt")
|
|
45
|
+
);
|
|
46
|
+
}
|