3

質問:

Regarding compiling TypeScript code server-side, is there a way to get a list of all the reference paths either for a single .ts file - or better, the whole compilation (starting from a single .ts file)? In order, preferably.

I'd prefer to use the existing parser if possible, rather than parsing the files with new code.

Context:

Since I don't think it exactly exists, I want to write:

  1. a server-side web user control that takes a .ts path and generates a cache-busting script tag pointing to
  2. an HttpHandler that compiles the requested .ts file ONCE at first request and then adds CacheDependencies to all the reference dependencies paths. When a file changes, the script-generating web user control updates it's cache-busting suffix of subsequent requests.

so in Release Mode, <tsb:typescript root="app.ts" runat="server" /> yields

<script type="text/javascript" src="app.ts?32490839"></script>

ここで、配信されるスクリプトは、オンデマンドのキャッシュされた単一ファイルスクリプトです。

また、デバッグモードでは、変更されていないタグは代わりに次のようになります。

<script type="text/javascript" src="dependency1.ts?32490839"></script>
<script type="text/javascript" src="dependency2.ts?32490839"></script>
<script type="text/javascript" src="app.ts?32490839"></script>

私が見た限りでは、この操作モードはTypeScriptVisualStudioプラグインやオプティマイザーバンドラーではサポートされていません。バンドラーは私が求めているものに近いものですが、キャッシュバストはなく、ファイルの明示的なバンドルを煩わせることなく単一ファイルをコンパイルすることはありません。

スクリプトのコンパイル中の最初のリクエストでパフォーマンスが低下してもかまいません。それに加えて、おそらくこのセットアップが存在してはならない、または存在できないという本当に大きな理由があります。これができない、または明らかにすべきでない場合は、その点での回答もいただければ幸いです。

私の解釈では、この欲求を中心に踊るStackOverflowに関する他の質問を見てきましたが、これほど明確なものはなく、関連する回答もありません。

ありがとう!

また、別のプロセスでtsc.exeを実行することは、私のHttpHandlerを実行時にコンパイルするための最良の方法ですか、それともこれをインプロセスで実行するための洗練された安全で簡単な方法がありますか?

4

3 に答える 3

1

最適化を有効にし、バンドルHTMLヘルパーを使用してバンドルをページに追加する場合、バンドルはJavaScriptに対してこれを行います。キャッシュバストURLはJavaScriptファイルの短いハッシュであるため、バンドル内のスクリプトを変更すると、新しいハッシュによってスクリプトがクライアントキャッシュから切り離されます。

コンパイルされたJavaScriptをバンドルし、バンドラーに縮小してキャッシュバストさせることをお勧めします。TypeScriptを取得して、outフラグを使用してすべての依存関係を持つ単一のファイルを生成できます...

tsc --out final.js app.ts

バンドルに含める必要があるのはfinal.jsだけです。これにより、すべてのファイルを明示的に一覧表示し、後で新しいファイルを追加する手間が省けます。

実行時にインターセプトしてコンパイルするものを作成することもできます。TypeScriptの利点の1つはコンパイル時のチェックであるため、実行前にこれを実行することをお勧めします。スクリプトがクライアントから要求されている場合、コンパイルエラーをどのように処理しますか。実行時に行う場合でも、.tsファイルではなくJavaScript参照をページに追加します。

于 2013-02-03T08:12:55.203 に答える
1

Madgeは、インタラクティブまたはノードで使用できます。

参照:特定のファイルから始まる完全なnodejs「require()」ツリーを確認するにはどうすればよいですか?

于 2021-06-16T01:39:07.777 に答える
1

2021年には--explainFiles

コードベースをより注意深く調べたい場合(タイプのみのインポートとランタイムインポートを区別するなど)、TypescriptAPIを使用できます。可能性は無限ですが、おそらく以下のこれらの部分はあなたを良い方向に導くのに役立ちます(おそらくバグがあります):

import * as ts from "typescript";

interface FoundReference {
    typeOnly: boolean;
    relativePathReference: boolean;
    referencingPath: string;
    referencedSpecifier: string;
}

const specifierRelativeFile = /^\..*(?<!\.(less|svg|png|woff))$/;
const specifierNodeModule = /^[^\.]/;

const diveDeeper = (path: string, node: ts.Node, found: FoundReference[]) =>
    Promise.all(node.getChildren().map(n => findAllReferencesNode(path, n, found)));

const findAllReferencesNode = async (path: string, node: ts.Node, found: FoundReference[]) => {
    switch (node.kind) {
        case ts.SyntaxKind.ExportDeclaration:
            const exportDeclaration = node as ts.ExportDeclaration;

            if (exportDeclaration.moduleSpecifier) {
                const specifier = (exportDeclaration.moduleSpecifier as ts.StringLiteral).text;

                if (specifier) {
                    if (specifierRelativeFile.test(specifier)) {
                        found.push({
                            typeOnly: exportDeclaration.isTypeOnly,
                            relativePathReference: true,
                            referencingPath: path,
                            referencedSpecifier: specifier
                        });
                    } else if (specifierNodeModule.test(specifier)) {
                        found.push({
                            typeOnly: exportDeclaration.isTypeOnly,
                            relativePathReference: false,
                            referencingPath: path,
                            referencedSpecifier: specifier
                        });
                    }
                }
            }

            break;
        case ts.SyntaxKind.ImportDeclaration:
            const importDeclaration = node as ts.ImportDeclaration;
            const importClause = importDeclaration.importClause;

            const specifier = (importDeclaration.moduleSpecifier as ts.StringLiteral).text;

            if (specifier) {
                if (specifierRelativeFile.test(specifier)) {
                    found.push({
                        typeOnly: (!!importClause && !importClause.isTypeOnly),
                        relativePathReference: true,
                        referencingPath: path,
                        referencedSpecifier: specifier
                    });
                } else if (specifierNodeModule.test(specifier)) {
                    found.push({
                        typeOnly: (!!importClause && !importClause.isTypeOnly),
                        relativePathReference: false,
                        referencingPath: path,
                        referencedSpecifier: specifier
                    });
                }
            }

            break;
        case ts.SyntaxKind.CallExpression:
            const callExpression = node as ts.CallExpression;

            if ((callExpression.expression.kind === ts.SyntaxKind.ImportKeyword ||
                (callExpression.expression.kind === ts.SyntaxKind.Identifier &&
                    callExpression.expression.getText() === "require")) &&
                callExpression.arguments[0]?.kind === ts.SyntaxKind.StringLiteral) {

                const specifier = (callExpression.arguments[0] as ts.StringLiteral).text;

                if (specifierRelativeFile.test(specifier)) {
                    found.push({
                        typeOnly: false,
                        relativePathReference: true,
                        referencingPath: path,
                        referencedSpecifier: specifier
                    });
                } else if (specifierNodeModule.test(specifier)) {
                    found.push({
                        typeOnly: false,
                        relativePathReference: false,
                        referencingPath: path,
                        referencedSpecifier: specifier
                    });
                } else {
                    await diveDeeper(path, node, found);
                }
            } else {
                await diveDeeper(path, node, found);
            }

            break;
        default:
            await diveDeeper(path, node, found);

            break;
    }
}

const path = "example.ts";

const source = `
import foo from "./foo";
import * as bar from "./bar";
import { buzz } from "./fizz/buzz";

export foo from "./foo";
export * as bar from "./bar";
export { buzz } from "./fizz/buzz";

const whatever = require("whatever");

const stuff = async () => {
    require("whatever");

    const x = await import("xyz");
}
`

const rootNode = ts.createSourceFile(
    path,
    source,
    ts.ScriptTarget.Latest,
    /*setParentNodes */ true
);

const found: FoundReference[] = [];

findAllReferencesNode(path, rootNode, found)
.then(() => { 
    console.log(found); 
});

[
  {
    "typeOnly": true,
    "relativePathReference": true,
    "referencingPath": "example.ts",
    "referencedSpecifier": "./foo"
  },
  {
    "typeOnly": true,
    "relativePathReference": true,
    "referencingPath": "example.ts",
    "referencedSpecifier": "./bar"
  },
  {
    "typeOnly": true,
    "relativePathReference": true,
    "referencingPath": "example.ts",
    "referencedSpecifier": "./fizz/buzz"
  },
  {
    "typeOnly": false,
    "relativePathReference": true,
    "referencingPath": "example.ts",
    "referencedSpecifier": "./bar"
  },
  {
    "typeOnly": false,
    "relativePathReference": true,
    "referencingPath": "example.ts",
    "referencedSpecifier": "./fizz/buzz"
  },
  {
    "typeOnly": false,
    "relativePathReference": false,
    "referencingPath": "example.ts",
    "referencedSpecifier": "whatever"
  },
  {
    "typeOnly": false,
    "relativePathReference": false,
    "referencingPath": "example.ts",
    "referencedSpecifier": "whatever"
  },
  {
    "typeOnly": false,
    "relativePathReference": false,
    "referencingPath": "example.ts",
    "referencedSpecifier": "xyz"
  }
] 

referenceSpecifierを取得したら、それを次のパスに解決し、次に解決されたファイルを使用して探索を繰り返すための基本的なロジックが必要です。

于 2021-09-16T14:46:44.263 に答える