@no22/dison 1.0.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/LICENSE +15 -0
- package/README.md +194 -0
- package/dist/analysis.d.ts +21 -0
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +267 -0
- package/dist/analysis.js.map +1 -0
- package/dist/ast.d.ts +46 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +5 -0
- package/dist/ast.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +98 -0
- package/dist/cli.js.map +1 -0
- package/dist/codegen.d.ts +10 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +181 -0
- package/dist/codegen.js.map +1 -0
- package/dist/collisions.d.ts +26 -0
- package/dist/collisions.d.ts.map +1 -0
- package/dist/collisions.js +120 -0
- package/dist/collisions.js.map +1 -0
- package/dist/core.d.ts +229 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +247 -0
- package/dist/core.js.map +1 -0
- package/dist/generated-runtime.d.ts +7 -0
- package/dist/generated-runtime.d.ts.map +1 -0
- package/dist/generated-runtime.js +51 -0
- package/dist/generated-runtime.js.map +1 -0
- package/dist/lexer.d.ts +16 -0
- package/dist/lexer.d.ts.map +1 -0
- package/dist/lexer.js +93 -0
- package/dist/lexer.js.map +1 -0
- package/dist/parser.d.ts +39 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +594 -0
- package/dist/parser.js.map +1 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +79 -0
- package/dist/runtime.js.map +1 -0
- package/package.json +56 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { transpileDisonToTS, findBindCollisions } from './core.js'; // ※NodeNext環境のために.jsをつけます
|
|
5
|
+
// 複数ファイルの生成コードが共有ランタイム(DI_REGISTRY/TYPE_BINDINGS等)を
|
|
6
|
+
// importする際に使うパッケージのサブパス。@no22/disonパッケージ自身が
|
|
7
|
+
// この下に実体(src/generated-runtime.ts→dist/generated-runtime.js)を
|
|
8
|
+
// 配布しているため、変換対象のプロジェクトは"@no22/dison"を依存関係として
|
|
9
|
+
// インストールしている必要がある(docs/packaging.md)。パッケージ名が
|
|
10
|
+
// "@no22/dison"なのはnpmの類似名ポリシー(jison/bson/json等と類似と
|
|
11
|
+
// 判定された)により無scope名"dison"では公開できなかったため。
|
|
12
|
+
const RUNTIME_PACKAGE_SPECIFIER = '@no22/dison/runtime';
|
|
13
|
+
function outputPathFor(inputFile) {
|
|
14
|
+
const ext = path.extname(inputFile);
|
|
15
|
+
return inputFile.replace(ext, '.ts');
|
|
16
|
+
}
|
|
17
|
+
// 単一ファイルの変換(従来通り)。ランタイムの前置き(DI_REGISTRY等)は
|
|
18
|
+
// 生成TSファイルにインラインされる。他ファイルとランタイム状態を共有する
|
|
19
|
+
// 必要が無い、これまで通りの使い方。
|
|
20
|
+
function transpileSingleFile(inputFile) {
|
|
21
|
+
const sourceCode = fs.readFileSync(inputFile, 'utf-8');
|
|
22
|
+
const generatedTS = transpileDisonToTS(sourceCode);
|
|
23
|
+
const outputFile = outputPathFor(inputFile);
|
|
24
|
+
fs.writeFileSync(outputFile, generatedTS, 'utf-8');
|
|
25
|
+
console.log(`🎉 Success: ${inputFile} ➔ ${outputFile}`);
|
|
26
|
+
}
|
|
27
|
+
// 複数ファイルの変換(複数ファイル対応フェーズ1・2、docs/multi-file-support.md、
|
|
28
|
+
// docs/bind-interface-token.md)。2段階の処理になっている:
|
|
29
|
+
//
|
|
30
|
+
// 1. 全入力ファイルを読み込み、bind/injectableの型名衝突を検出する
|
|
31
|
+
// (findBindCollisions、案D)。1件でも見つかればどのファイルも
|
|
32
|
+
// 書き出さずに中断する(部分的に生成された不整合な状態を残さないため)。
|
|
33
|
+
// 2. 問題が無ければ、各ファイルを独立にトランスパイルする。ランタイムの
|
|
34
|
+
// 前置き(DI_REGISTRY/TYPE_BINDINGS等)は各生成ファイルが
|
|
35
|
+
// "@no22/dison/runtime" パッケージから直接importする。これによりファイルを跨いだ
|
|
36
|
+
// override/bind/activateが正しく効くようになる(1ファイルずつ独立に
|
|
37
|
+
// トランスパイルすると、それぞれが自分専用のDI_REGISTRY/TYPE_BINDINGSを
|
|
38
|
+
// 持ってしまい、互いに影響しない)。以前はこのランタイムを出力先
|
|
39
|
+
// ディレクトリにdison-runtime.tsとして毎回書き出していたが、disonの
|
|
40
|
+
// パッケージ化に伴いパッケージ自身から読み込む方式に変更した
|
|
41
|
+
// (docs/packaging.md)。
|
|
42
|
+
//
|
|
43
|
+
// 以前は共有ランタイム(dison-runtime.ts)をどのディレクトリに書き出すか
|
|
44
|
+
// 曖昧にならないよう、全入力ファイルが同一ディレクトリにあることを要求
|
|
45
|
+
// していた。パッケージ化(docs/packaging.md)でこの書き出し自体をやめ、
|
|
46
|
+
// "@no22/dison/runtime" パッケージから直接importする方式に変更したため、
|
|
47
|
+
// その制約の根拠は無くなった。各ファイルは自身のパスを基準に個別に
|
|
48
|
+
// 入出力されるため、異なるディレクトリの入力ファイルを混在させても良い。
|
|
49
|
+
function transpileMultipleFiles(inputFiles) {
|
|
50
|
+
const fileInputs = inputFiles.map((inputFile) => ({
|
|
51
|
+
path: inputFile,
|
|
52
|
+
source: fs.readFileSync(inputFile, 'utf-8'),
|
|
53
|
+
}));
|
|
54
|
+
const collisions = findBindCollisions(fileInputs);
|
|
55
|
+
if (collisions.length > 0) {
|
|
56
|
+
const details = collisions.map((c) => ` - ${c.message}`).join('\n');
|
|
57
|
+
throw new Error(`Possible bind/injectable type-name collisions across files` +
|
|
58
|
+
` (${collisions.length}). No files were generated.\n${details}`);
|
|
59
|
+
}
|
|
60
|
+
console.log(`🎉 Using "${RUNTIME_PACKAGE_SPECIFIER}" as the shared runtime (the target project must have "@no22/dison" installed as a dependency).`);
|
|
61
|
+
for (const file of fileInputs) {
|
|
62
|
+
const generatedTS = transpileDisonToTS(file.source, { runtimeModulePath: RUNTIME_PACKAGE_SPECIFIER });
|
|
63
|
+
const outputFile = outputPathFor(file.path);
|
|
64
|
+
fs.writeFileSync(outputFile, generatedTS, 'utf-8');
|
|
65
|
+
console.log(`🎉 Success: ${file.path} ➔ ${outputFile}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function main() {
|
|
69
|
+
// 引数の取得 (例: npx tsx src/cli.ts test.dis / npx tsx src/cli.ts a.dis b.dis)
|
|
70
|
+
// process.argv[0]はnode、[1]はスクリプトパス、[2]以降がユーザーが渡したファイル名
|
|
71
|
+
const inputFiles = process.argv.slice(2);
|
|
72
|
+
if (inputFiles.length === 0) {
|
|
73
|
+
console.error("❌ Error: please specify an input file.");
|
|
74
|
+
console.log("👉 Usage: dison <file.dis> [<file2.dis> ...]");
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
for (const inputFile of inputFiles) {
|
|
79
|
+
if (!fs.existsSync(inputFile)) {
|
|
80
|
+
console.error(`❌ Error: file '${inputFile}' not found.`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (inputFiles.length === 1) {
|
|
85
|
+
transpileSingleFile(inputFiles[0]);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
transpileMultipleFiles(inputFiles);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.error("❌ Compilation failed:");
|
|
93
|
+
console.error(error.message);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
main();
|
|
98
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Node } from "./ast.js";
|
|
2
|
+
export interface FromActivateBinding {
|
|
3
|
+
exportedName: string;
|
|
4
|
+
alias: string;
|
|
5
|
+
path: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveFromActivateBindings(nodes: Node[]): Map<string, FromActivateBinding>;
|
|
8
|
+
export declare function generateFromImportStatements(bindings: Map<string, FromActivateBinding>): string;
|
|
9
|
+
export declare function generate(nodes: Node[], fromBindings: Map<string, FromActivateBinding>): string;
|
|
10
|
+
//# sourceMappingURL=codegen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAA4B,MAAM,UAAU,CAAC;AA+G/D,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAqB3F;AAOD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,MAAM,CAO/F;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,MAAM,CAsC9F"}
|
package/dist/codegen.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// =====================================================================
|
|
2
|
+
// 4. CodeGenerator
|
|
3
|
+
// =====================================================================
|
|
4
|
+
import { isSimpleTypeShape } from "./analysis.js";
|
|
5
|
+
// override 1件分のDI_REGISTRY代入文を生成する。configuration内(関数本体に
|
|
6
|
+
// 収集される場合)と、configurationで包まない単独のoverride(その場に直接
|
|
7
|
+
// 出力される場合)の両方から共有される。
|
|
8
|
+
function generateOverrideEntryLines(entry) {
|
|
9
|
+
// entry.className はクラスの実体を指す識別子としてそのまま埋め込む
|
|
10
|
+
// (文字列リテラルにはしない)。registerOverride がクラスの実体そのものを
|
|
11
|
+
// キーにするため、className がスコープ内に存在しない(タイプミス・
|
|
12
|
+
// importし忘れ)場合は tsc が "Cannot find name" として検出する。
|
|
13
|
+
return entry.assignments.map((a) => ` registerOverride(${entry.className}, "${a.prop}", () => (${a.valueExpr}));`);
|
|
14
|
+
}
|
|
15
|
+
// bind 1件分のbindType呼び出し文を生成する(generateOverrideEntryLinesと同様、
|
|
16
|
+
// configuration内・単独bindの両方から共有される)。
|
|
17
|
+
//
|
|
18
|
+
// 型(クラス/インターフェース/型エイリアス/abstract class、ジェネリクスの
|
|
19
|
+
// 具体的なインスタンス化を含む)単位の横断的な差し替え。
|
|
20
|
+
//
|
|
21
|
+
// 型安全性: bindType<T> の型引数に差し替え元の型を明示的に指定することで、
|
|
22
|
+
// 差し替え先のfactoryがその型と互換でなければ tsc がコンパイルエラーにする。
|
|
23
|
+
// 差し替え元はインターフェースや型エイリアスでもよい(型引数としてのみ使われ、
|
|
24
|
+
// 実行時の値としては要求されないため)。差し替え先は `new` で実体化する必要が
|
|
25
|
+
// あるため、実質的に具象クラスに限定される(インターフェースを指定すると
|
|
26
|
+
// `new` の時点でtscがエラーにする)。ジェネリクスの具体的なインスタンス化
|
|
27
|
+
// (例: Repository<User>)も通常の型表現としてそのまま型引数・new式に渡せる。
|
|
28
|
+
//
|
|
29
|
+
// 照合キー(originalTypeKey/replacementTypeKey)は空白・コメントを除いた
|
|
30
|
+
// 正規化済みの文字列を使う。injectable側のresolveTypeキーと同じ規則
|
|
31
|
+
// (parseTypeExpr/parseGenericTypeRefのtypeKey生成)で作っているため、
|
|
32
|
+
// 書き方の空白差("Repository<User>" 対 "Repository <User>" など)に
|
|
33
|
+
// 影響されずに一致する。ただし"as <トークン>"句が指定されている場合は、
|
|
34
|
+
// 文字列キーの代わりにそのトークンの識別子参照をそのままキーとして使う
|
|
35
|
+
// (複数ファイルにまたがる同名interface/型エイリアスの衝突を避けるため。
|
|
36
|
+
// docs/bind-interface-token.md)。
|
|
37
|
+
//
|
|
38
|
+
// 連鎖: 差し替え先クラス自身がさらに bind されている可能性があるので、
|
|
39
|
+
// 直接 new せず resolveType を介して再帰的に解決する。
|
|
40
|
+
// (bind A = B; bind B = C; と書けば、Aの解決は最終的にCまで辿る)
|
|
41
|
+
function generateBindEntryLines(entry) {
|
|
42
|
+
const originalKeyExpr = entry.token !== undefined ? entry.token : JSON.stringify(entry.originalTypeKey);
|
|
43
|
+
const replacementKey = JSON.stringify(entry.replacementTypeKey);
|
|
44
|
+
return [
|
|
45
|
+
` bindType<${entry.originalTypeName}>(${originalKeyExpr}, () => resolveType(${replacementKey}, () => new ${entry.replacementTypeName}()));`,
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
function generateConfiguration(node) {
|
|
49
|
+
const lines = [];
|
|
50
|
+
for (const entry of node.entries) {
|
|
51
|
+
if (entry.kind === "override") {
|
|
52
|
+
lines.push(...generateOverrideEntryLines(entry));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
lines.push(...generateBindEntryLines(entry));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// export しておくことで、他ファイルから import { activateName } して
|
|
59
|
+
// activate をクロスファイルで使えるようにする(docs/multi-file-support.md
|
|
60
|
+
// フェーズ1。単一ファイル利用時もexportは無害で、同一ファイル内から
|
|
61
|
+
// 従来通り呼び出せる)。
|
|
62
|
+
return `export function activate${node.name}() {\n${lines.join("\n")}\n}`;
|
|
63
|
+
}
|
|
64
|
+
function generateInjectable(node) {
|
|
65
|
+
const { propName: p, typeName: t, typeKey: k, defaultExpr, token } = node;
|
|
66
|
+
const simpleShape = isSimpleTypeShape(k);
|
|
67
|
+
// パーサ(parseInjectable)が「危険な型(isRiskyInjectableType)には
|
|
68
|
+
// defaultExpr が必須」であることを既に強制しているため、ここでは
|
|
69
|
+
// defaultExpr が無ければ安全な型(new T() で問題なく生成できる)と
|
|
70
|
+
// 分かっている。危険な型の判定・強制ロジックをここで再度持たない。
|
|
71
|
+
const finalDefault = defaultExpr !== undefined ? `(${defaultExpr})` : `new ${t}()`;
|
|
72
|
+
// bind (TYPE_BINDINGS) は型注釈が識別子(+ジェネリクス)の形をしていれば
|
|
73
|
+
// 試みる。interfaceや型エイリアスも文字列キーとして扱えるため、これ自体は
|
|
74
|
+
// コンパイル上安全。実際にbindされていなければ finalDefault にフォールバック
|
|
75
|
+
// する。優先順位: プロパティ単位の override > 型単位の bind > 宣言済みの既定
|
|
76
|
+
// 初期化式。照合キーには typeName ではなく正規化済みの typeKey を使う
|
|
77
|
+
// (bind側のキー生成と同じ規則にそろえ、空白の書き方差で一致しなくなるのを
|
|
78
|
+
// 防ぐ。詳細はファイル冒頭のヘッダーコメント参照)。"as <トークン>"句が
|
|
79
|
+
// 指定されている場合は、文字列キーの代わりにそのトークンの識別子参照を
|
|
80
|
+
// そのままキーとして使う(docs/bind-interface-token.md)。
|
|
81
|
+
const keyExpr = token !== undefined ? token : JSON.stringify(k);
|
|
82
|
+
const fallback = simpleShape ? `resolveType(${keyExpr}, () => ${finalDefault})` : finalDefault;
|
|
83
|
+
// strictモードでは `if (!this._x) { this._x = f(); }` の後の `return this._x;` は
|
|
84
|
+
// 途中で挟まる関数呼び出し(factory())によりnarrowingが無効化され、
|
|
85
|
+
// `T | undefined` のままとみなされてコンパイルエラーになる。
|
|
86
|
+
// そのためローカル変数を経由して確実に非undefined型として返す。
|
|
87
|
+
return [
|
|
88
|
+
``,
|
|
89
|
+
` private _${p}?: ${t};`,
|
|
90
|
+
` get ${p}(): ${t} {`,
|
|
91
|
+
` if (!this._${p}) {`,
|
|
92
|
+
` const factory = getOverride(this.constructor, "${p}");`,
|
|
93
|
+
` this._${p} = factory ? factory() : ${fallback};`,
|
|
94
|
+
` }`,
|
|
95
|
+
` return this._${p}!;`,
|
|
96
|
+
` }`,
|
|
97
|
+
` set ${p}(value: ${t}) { this._${p} = value; }`,
|
|
98
|
+
``,
|
|
99
|
+
].join("\n");
|
|
100
|
+
}
|
|
101
|
+
// (name, path) の組ごとにimport時のエイリアスを割り当てる。同じ組み合わせは
|
|
102
|
+
// 同じエイリアスを再利用する(1つのimportにまとめる)。異なるpathが同じ
|
|
103
|
+
// configuration名を持つ場合(複数ファイルにたまたま同名のconfigurationが
|
|
104
|
+
// ある場合)は、エイリアスに連番を振って衝突を避ける
|
|
105
|
+
// (docs/activate-from-syntax.md参照)。
|
|
106
|
+
export function resolveFromActivateBindings(nodes) {
|
|
107
|
+
const byKey = new Map();
|
|
108
|
+
const aliasOwner = new Map(); // alias -> それを最初に使ったpath
|
|
109
|
+
for (const node of nodes) {
|
|
110
|
+
if (node.kind !== "activate" || node.fromPath === undefined)
|
|
111
|
+
continue;
|
|
112
|
+
const key = `${node.name}::${node.fromPath}`;
|
|
113
|
+
if (byKey.has(key))
|
|
114
|
+
continue;
|
|
115
|
+
const exportedName = `activate${node.name}`;
|
|
116
|
+
let alias = exportedName;
|
|
117
|
+
let suffix = 2;
|
|
118
|
+
while (aliasOwner.has(alias) && aliasOwner.get(alias) !== node.fromPath) {
|
|
119
|
+
alias = `${exportedName}_${suffix}`;
|
|
120
|
+
suffix++;
|
|
121
|
+
}
|
|
122
|
+
aliasOwner.set(alias, node.fromPath);
|
|
123
|
+
byKey.set(key, { exportedName, alias, path: node.fromPath });
|
|
124
|
+
}
|
|
125
|
+
return byKey;
|
|
126
|
+
}
|
|
127
|
+
// resolveFromActivateBindingsの結果からimport文をまとめて生成する。
|
|
128
|
+
// ES Modulesのimportはファイル先頭にしか書けないため、"activate ... from"が
|
|
129
|
+
// ソース中のどこに現れても、importはここでまとめてファイル先頭に出力し、
|
|
130
|
+
// 各activateノード自体は呼び出し文だけを残す(generate関数のactivateの
|
|
131
|
+
// 分岐を参照)。
|
|
132
|
+
export function generateFromImportStatements(bindings) {
|
|
133
|
+
if (bindings.size === 0)
|
|
134
|
+
return "";
|
|
135
|
+
const lines = [...bindings.values()].map(({ exportedName, alias, path }) => {
|
|
136
|
+
const spec = alias === exportedName ? exportedName : `${exportedName} as ${alias}`;
|
|
137
|
+
return `import { ${spec} } from ${JSON.stringify(path)};`;
|
|
138
|
+
});
|
|
139
|
+
return lines.join("\n") + "\n\n";
|
|
140
|
+
}
|
|
141
|
+
export function generate(nodes, fromBindings) {
|
|
142
|
+
let out = "";
|
|
143
|
+
for (const node of nodes) {
|
|
144
|
+
switch (node.kind) {
|
|
145
|
+
case "raw":
|
|
146
|
+
out += node.text;
|
|
147
|
+
break;
|
|
148
|
+
case "configuration":
|
|
149
|
+
out += generateConfiguration(node);
|
|
150
|
+
break;
|
|
151
|
+
case "injectable":
|
|
152
|
+
out += generateInjectable(node);
|
|
153
|
+
break;
|
|
154
|
+
case "token":
|
|
155
|
+
// 複数箇所(複数ファイルにまたがる場合も含む)から安定して参照
|
|
156
|
+
// される共有の一意な値として、Symbolを生成する
|
|
157
|
+
// (docs/bind-interface-token.md)。
|
|
158
|
+
out += `export const ${node.name} = Symbol(${JSON.stringify(node.name)});`;
|
|
159
|
+
break;
|
|
160
|
+
case "activate":
|
|
161
|
+
if (node.fromPath !== undefined) {
|
|
162
|
+
const binding = fromBindings.get(`${node.name}::${node.fromPath}`);
|
|
163
|
+
out += `${binding.alias}();`;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
out += `activate${node.name}();`;
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
case "standalone-override":
|
|
170
|
+
// configurationで包まないため関数宣言にせず、その場に直接
|
|
171
|
+
// 代入文として出力する(書かれた位置で即座に実行される)。
|
|
172
|
+
out += generateOverrideEntryLines(node.entry).join("\n");
|
|
173
|
+
break;
|
|
174
|
+
case "standalone-bind":
|
|
175
|
+
out += generateBindEntryLines(node.entry).join("\n");
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=codegen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codegen.js","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface DisonFileInput {
|
|
2
|
+
path: string;
|
|
3
|
+
source: string;
|
|
4
|
+
}
|
|
5
|
+
export interface BindCollisionDiagnostic {
|
|
6
|
+
name: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 複数の.disファイルを横断して、`bind`の左辺や`injectable`の型注釈に
|
|
11
|
+
* 使われている型名(クラス/interface/型エイリアスを問わない)の衝突を
|
|
12
|
+
* 検出する(docs/bind-interface-token.md、案D)。
|
|
13
|
+
*
|
|
14
|
+
* 各ファイルをそれぞれパースし、候補となる型名(bindの左辺、および
|
|
15
|
+
* 識別子+ジェネリクスの形をしたinjectableの型注釈)ごとに、その出現が
|
|
16
|
+
* 「このファイル自身のローカル宣言」か「importならそのspecifier文字列」
|
|
17
|
+
* かという由来を集める。同じ型名について異なる由来が2つ以上見つかった
|
|
18
|
+
* 場合、"as <トークン>" 句が付いていない出現箇所すべてを診断として
|
|
19
|
+
* 返す(衝突が無い、または全出現箇所にトークンが付いていれば空配列)。
|
|
20
|
+
*
|
|
21
|
+
* この検証は複数ファイルの情報が揃って初めて意味を持つため、単一ファイルの
|
|
22
|
+
* `transpileDisonToTS`自体はこのチェックを行わない(CLIが複数ファイルを
|
|
23
|
+
* 処理する際にこの関数を呼び出す)。
|
|
24
|
+
*/
|
|
25
|
+
export declare function findBindCollisions(files: DisonFileInput[]): BindCollisionDiagnostic[];
|
|
26
|
+
//# sourceMappingURL=collisions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collisions.d.ts","sourceRoot":"","sources":["../src/collisions.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAoBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,uBAAuB,EAAE,CA+FrF"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// =====================================================================
|
|
2
|
+
// 複数ファイルにまたがる bind/injectable の型名衝突検出(案D)
|
|
3
|
+
// =====================================================================
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { Lexer } from "./lexer.js";
|
|
6
|
+
import { collectDeclaredTypeKinds, collectImportOrigins, isSimpleTypeShape, baseIdentifierOf } from "./analysis.js";
|
|
7
|
+
import { Parser } from "./parser.js";
|
|
8
|
+
// ファイルパスを「絶対パス化し、拡張子を除いた」形に正規化する。
|
|
9
|
+
// findBindCollisionsで、あるファイルのローカル宣言と、それを相対パスで
|
|
10
|
+
// 正しくimportしている別ファイルとを同じ実体として一致させるために使う
|
|
11
|
+
// (拡張子(.dis/.ts/無し)の書き方の違いを吸収する)。
|
|
12
|
+
function normalizeExtensionlessAbsolutePath(filePath) {
|
|
13
|
+
const abs = path.resolve(filePath);
|
|
14
|
+
const ext = path.extname(abs);
|
|
15
|
+
return ext ? abs.slice(0, -ext.length) : abs;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 複数の.disファイルを横断して、`bind`の左辺や`injectable`の型注釈に
|
|
19
|
+
* 使われている型名(クラス/interface/型エイリアスを問わない)の衝突を
|
|
20
|
+
* 検出する(docs/bind-interface-token.md、案D)。
|
|
21
|
+
*
|
|
22
|
+
* 各ファイルをそれぞれパースし、候補となる型名(bindの左辺、および
|
|
23
|
+
* 識別子+ジェネリクスの形をしたinjectableの型注釈)ごとに、その出現が
|
|
24
|
+
* 「このファイル自身のローカル宣言」か「importならそのspecifier文字列」
|
|
25
|
+
* かという由来を集める。同じ型名について異なる由来が2つ以上見つかった
|
|
26
|
+
* 場合、"as <トークン>" 句が付いていない出現箇所すべてを診断として
|
|
27
|
+
* 返す(衝突が無い、または全出現箇所にトークンが付いていれば空配列)。
|
|
28
|
+
*
|
|
29
|
+
* この検証は複数ファイルの情報が揃って初めて意味を持つため、単一ファイルの
|
|
30
|
+
* `transpileDisonToTS`自体はこのチェックを行わない(CLIが複数ファイルを
|
|
31
|
+
* 処理する際にこの関数を呼び出す)。
|
|
32
|
+
*/
|
|
33
|
+
export function findBindCollisions(files) {
|
|
34
|
+
const occurrencesByName = new Map();
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
const tokens = new Lexer(file.source).tokenize();
|
|
37
|
+
const typeKinds = collectDeclaredTypeKinds(tokens);
|
|
38
|
+
const importOrigins = collectImportOrigins(tokens);
|
|
39
|
+
const ast = new Parser(tokens, typeKinds).parseProgram();
|
|
40
|
+
const originOf = (name) => {
|
|
41
|
+
if (typeKinds.classNames.has(name) || typeKinds.nonNewableTypeNames.has(name)) {
|
|
42
|
+
return { key: `P::${normalizeExtensionlessAbsolutePath(file.path)}`, label: `local declaration in ${file.path}` };
|
|
43
|
+
}
|
|
44
|
+
const spec = importOrigins.get(name);
|
|
45
|
+
if (spec !== undefined) {
|
|
46
|
+
if (spec.startsWith(".")) {
|
|
47
|
+
// 相対importは、importしている側のファイルの位置を基準に解決し、
|
|
48
|
+
// 拡張子を除いた絶対パスに正規化する。これにより、あるファイルの
|
|
49
|
+
// ローカル宣言と、それを相対パスで正しくimportしている別ファイルとが、
|
|
50
|
+
// 同じ実体として一致するようになる(無関係な由来だと誤検出しない)。
|
|
51
|
+
const resolved = path.resolve(path.dirname(file.path), spec);
|
|
52
|
+
return {
|
|
53
|
+
key: `P::${normalizeExtensionlessAbsolutePath(resolved)}`,
|
|
54
|
+
label: `import of "${spec}" (from ${file.path})`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// bareなパッケージ指定子("some-package"等)は解決しようがないため、
|
|
58
|
+
// 指定子の文字列そのものを由来とする(異なるパッケージ名は
|
|
59
|
+
// 意図的に別の由来として扱う。docs/bind-interface-token.md 2.4節)。
|
|
60
|
+
return { key: `PKG::${spec}`, label: `import from "${spec}"` };
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
};
|
|
64
|
+
const record = (name, describe, hasToken) => {
|
|
65
|
+
const origin = originOf(name);
|
|
66
|
+
const list = occurrencesByName.get(name) ?? [];
|
|
67
|
+
list.push({
|
|
68
|
+
file: file.path,
|
|
69
|
+
describe,
|
|
70
|
+
hasToken,
|
|
71
|
+
originKey: origin ? origin.key : null,
|
|
72
|
+
originLabel: origin ? origin.label : "unknown origin",
|
|
73
|
+
});
|
|
74
|
+
occurrencesByName.set(name, list);
|
|
75
|
+
};
|
|
76
|
+
const visitBindEntry = (entry, describe) => {
|
|
77
|
+
const base = baseIdentifierOf(entry.originalTypeKey);
|
|
78
|
+
record(base, describe, entry.token !== undefined);
|
|
79
|
+
};
|
|
80
|
+
for (const node of ast) {
|
|
81
|
+
if (node.kind === "injectable") {
|
|
82
|
+
if (isSimpleTypeShape(node.typeKey)) {
|
|
83
|
+
record(baseIdentifierOf(node.typeKey), `injectable "${node.propName}"`, node.token !== undefined);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (node.kind === "configuration") {
|
|
87
|
+
for (const entry of node.entries) {
|
|
88
|
+
if (entry.kind === "bind") {
|
|
89
|
+
visitBindEntry(entry, `bind "${entry.originalTypeName}" inside configuration "${node.name}"`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (node.kind === "standalone-bind") {
|
|
94
|
+
visitBindEntry(node.entry, `a standalone bind "${node.entry.originalTypeName}"`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const diagnostics = [];
|
|
99
|
+
for (const [name, occurrences] of occurrencesByName) {
|
|
100
|
+
// トークン済みの出現は文字列キーの共有プールから既に抜けているため、
|
|
101
|
+
// 衝突判定・報告のどちらにおいても対象から除外する
|
|
102
|
+
// (そのファイルは既に安全なので、他のファイルが後からトークンを
|
|
103
|
+
// 使い始めても再度フラグされない)。
|
|
104
|
+
const untokened = occurrences.filter((o) => !o.hasToken);
|
|
105
|
+
const distinctOriginKeys = new Set(untokened.map((o) => o.originKey).filter((k) => k !== null));
|
|
106
|
+
if (distinctOriginKeys.size <= 1)
|
|
107
|
+
continue; // 由来が1つ(または不明のみ)なら衝突なし
|
|
108
|
+
const originsDescription = [...new Set(untokened.map((o) => o.originLabel))].join(" / ");
|
|
109
|
+
for (const occ of untokened) {
|
|
110
|
+
diagnostics.push({
|
|
111
|
+
name,
|
|
112
|
+
message: `${occ.file}'s ${occ.describe}: type "${name}" is used from multiple origins (${originsDescription})` +
|
|
113
|
+
` and may collide across files.` +
|
|
114
|
+
` Use "as <token>" to disambiguate explicitly.`,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return diagnostics;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=collisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collisions.js","sourceRoot":"","sources":["../src/collisions.ts"],"names":[],"mappings":"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"}
|