一般的なクラスの共有ノードモジュールの使用


15

ゴール

だから私はこの構造を持つプロジェクトを持っています:

  • ionic-app
  • firebase-functions
  • 共有しました

目標は、sharedモジュールに共通のインターフェースとクラスを定義することです。

制限事項

ローカルで使用するためにコードをnpmにアップロードしたくないので、コードをアップロードする予定はありません。100%オフラインで動作するはずです。

開発プロセスはオフラインで動作するはずですが、ionic-appおよびfirebase-functionsモジュールはfirebase(ホスティングおよび機能)にデプロイされます。したがって、sharedモジュールのコードはそこで利用できるはずです。

これまでに試したこと

  • typescriptでProject Referencesを使用してみましたが、機能するまでには至っていません
  • この質問の 2番目の回答のように、npmモジュールとしてインストールして試してみました
    • 最初は問題なく動作しているようですが、ビルド中に実行するとfirebase deploy次のようなエラーが発生します。
Function failed on loading user code. Error message: Code in file lib/index.js can't be loaded.
Did you list all required modules in the package.json dependencies?
Detailed stack trace: Error: Cannot find module 'shared'
    at Function.Module._resolveFilename (module.js:548:15)
    at Function.Module._load (module.js:475:25)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/srv/lib/index.js:5:18)

質問

typescripts configまたはNPMを使用して共有モジュールを作成するためのソリューションはありますか?

これを重複としてマークしないでください→StackOverflowで見つけた解決策を試しました。

追加情報

共有の構成:

// package.json
{
  "name": "shared",
  "version": "1.0.0",
  "description": "",
  "main": "dist/src/index.js",
  "types": "dist/src/index.d.ts",
  "files": [
    "dist/src/**/*"
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "publishConfig": {
    "access": "private"
  }
}

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "rootDir": ".",
    "sourceRoot": "src",
    "outDir": "dist",
    "sourceMap": true,
    "declaration": true,
    "target": "es2017"
  }
}

関数の構成:

// package.json
{
  "name": "functions",
  "scripts": {
    "lint": "tslint --project tsconfig.json",
    "build": "tsc",
    "serve": "npm run build && firebase serve --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "8"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "^8.0.0",
    "firebase-functions": "^3.1.0",
    "shared": "file:../../shared"
  },
  "devDependencies": {
    "@types/braintree": "^2.20.0",
    "tslint": "^5.12.0",
    "typescript": "^3.2.2"
  },
  "private": true
}


// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./",
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": false,
    "rootDir": "src",
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017"
  }
}

現在のsoution

npmスクリプトを共有モジュールに追加しました。これにより、すべてのファイル(index.jsなし)が他のモジュールにコピーされます。これには問題があり、重複したコードをSCMにチェックインし、変更のたびにそのコマンドを実行する必要があります。また、IDEはそれを別のファイルとして扱います。

回答:


4

序文: Typescriptのコンパイルのしくみと方法についてあまり詳しくないpackage.jsonに、ようなモジュールでどのように定義されるべき。このソリューションは機能しますが、目前のタスクを達成するためのハックな方法と考えることができます。

次のディレクトリ構造を想定します。

project/
  ionic-app/
    package.json
  functions/
    src/
      index.ts
    lib/
      index.js
    package.json
  shared/
    src/
      shared.ts
    lib/
      shared.js
    package.json

Firebaseサービスをデプロイするときに、デプロイ前フックとデプロ​​イ後フックにコマンドをアタッチできます。これはfirebase.json、プロパティpredeploypostdeploy目的のサービスで行われます。これらのプロパティには、コードをデプロイする前と後にそれぞれ実行する一連のコマンドの配列が含まれています。さらに、これらのコマンドは、環境変数RESOURCE_DIR./functionsまたはのディレクトリパス、./ionic-app該当する方)とPROJECT_DIRfirebase.json

inside のpredeploy配列を使用して、共有ライブラリのコードをCloud Functionsインスタンスにデプロイされているフォルダーにコピーできます。これを行うことにより、サブフォルダにあるライブラリのように共有コードを含めるか、Typescriptのパスマッピングを使用してその名前を名前付きモジュールにマッピングできます(そのため、を使用できます)。functionsfirebase.jsontsconfig.jsonimport { hiThere } from 'shared';

predeployフックの定義(グローバル用途はのインストールshxのWindowsとの互換性のために):

// firebase.json
{
  "functions": {
    "predeploy": [
      "shx rm -rf \"$RESOURCE_DIR/src/shared\"", // delete existing files
      "shx cp -R \"$PROJECT_DIR/shared/.\" \"$RESOURCE_DIR/src/shared\"", // copy latest version
      "npm --prefix \"$RESOURCE_DIR\" run lint", // lint & compile
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ]
  },
  "hosting": {
    "public": "ionic-app",
    ...
  }
}

コピーしたライブラリのtypescriptソースを関数のtypescriptコンパイラ設定にリンクします。

// functions/tsconfig.json
{
  "compilerOptions": {
    ...,
    "baseUrl": "./src",
    "paths": {
      "shared": ["shared/src"]
    }
  },
  "include": [
    "src"
  ],
  ...
}

コピーしたライブラリのパッケージフォルダーにモジュール名「共有」を関連付けます。

// functions/package.json
{
  "name": "functions",
  "scripts": {
    ...
  },
  "engines": {
    "node": "8"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "^8.6.0",
    "firebase-functions": "^3.3.0",
    "shared": "file:./src/shared",
    ...
  },
  "devDependencies": {
    "tslint": "^5.12.0",
    "typescript": "^3.2.2",
    "firebase-functions-test": "^0.1.6"
  },
  "private": true
}

同じ方法をホスティングフォルダーでも使用できます。


うまくいけば、これにより、Typescriptのコンパイルに詳しい人が、これらのフックを利用するよりクリーンなソリューションを考え出すことができます。


3

複数のパッケージを持つJavaScript(およびTypeScript)プロジェクトを管理するためのツールであるLernaを試してみてください。

セットアップ

プロジェクトが次のディレクトリ構造を持っていると仮定します。

packages
  ionic-app
    package.json
  firebase-functions
    package.json
  shared
    package.json

公開したくないすべてのモジュールと、モジュール内のエントリに正しいアクセスレベル(privateおよびconfig/accessキー)を指定typingsしてくださいshared

共有:

{
  "name": "shared",
  "version": "1.0.0",
  "private": true,
  "config": {
    "access": "private"
  },
  "main": "lib/index.js",
  "typings": "lib/index.d.ts",
  "scripts": {
    "compile": "tsc --project tsconfig.json"
  }
}

Ionic-app:

{
  "name": "ionic-app",
  "version": "1.0.0",
  "private": true,
  "config": {
    "access": "private"
  },
  "main": "lib/index.js",
  "scripts": {
    "compile": "tsc --project tsconfig.json"
  },
  "dependencies": {
    "shared": "1.0.0"
  }
}

上記の変更を行うと、ユニットテストフレームワーク、tslintなど、すべてのプロジェクトモジュールにアクセスさせたいルートレベルpackage.jsonを指定できるルートレベルを作成できますdevDependencies

packages
  ionic-app
    package.json
  firebase-functions
    package.json
  shared
    package.json
package.json         // root-level, same as the `packages` dir

このルートレベルpackage.jsonを使用して、プロジェクトのモジュール内の対応するスクリプトを(lerna経由で)呼び出すnpmスクリプトを定義することもできます。

{
  "name": "my-project",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "compile": "lerna run compile --stream",
    "postinstall": "lerna bootstrap",
  },
  "devDependencies": {
    "lerna": "^3.18.4",
    "tslint": "^5.20.1",
    "typescript": "^3.7.2"
  },
}

これを配置したら、lerna構成ファイルをルートディレクトリに追加します。

packages
  ionic-app
    package.json
  firebase-functions
    package.json
  shared
    package.json
package.json
lerna.json

次の内容で:

{
  "lerna": "3.18.4",
  "loglevel": "info",
  "packages": [
    "packages/*"
  ],
  "version": "1.0.0"
}

これnpm installで、ルートディレクトリで実行すると、ルートpostinstallレベルで定義されたスクリプトpackage.jsonがを呼び出しlerna bootstrapます。

lerna bootstrap行うことは、それはあなたのシンボリックリンクということであるsharedにモジュールをionic-app/node_modules/sharedしてfirebase-functions/node_modules/shared、ので、それらの2つのモジュールの観点からsharedだけで、他のNPMモジュールのように見えます。

コンパイル

もちろん、モジュールをシンボリックリンクするだけでは十分ではありません。モジュールをTypeScriptからJavaScriptにコンパイルする必要があるからです。

ここで、ルートレベルのpackage.json compileスクリプトが機能します。

npm run compileプロジェクトルートで実行すると、npmはを呼び出しlerna run compile --stream、各モジュールのファイルでlerna run compile --stream呼び出されたスクリプトを呼び出します。compilepackage.json

モジュールごとに独自のcompileスクリプトが作成されたので、モジュールごとにtsonfig.jsonファイルを作成できます。複製が気に入らない場合は、ルートレベルのtsconfig、またはルートレベルのtsconfigファイルとモジュールレベルのtsconfigファイルの組み合わせをルートから継承することで回避できます。

この設定が実際のプロジェクトでどのように機能するかを確認したい場合は、私がかなり幅広く使用しているSerenity / JSをご覧ください。

配備

under および、およびunder underプロジェクトルートでsharedシンボリックリンクされたモジュールの良い点は、コンシューマーモジュールをどこかに(たとえば、)デプロイする必要がある場合、それをすべて一緒に圧縮して心配する必要がないことです。展開前に開発依存関係を削除する必要があります。node_modulesfirebase-functionsionic-appdevDepedenciesnode_modulesionic-appnode_modules

お役に立てれば!

ヤン


夢中!私は間違いなくそれをチェックアウトし、これが適切であるかどうかを調べます。
MauriceNino

2

gitを使用してコードを管理している場合に考えられる別の解決策は、を使用することですgit submodule。を使用git submoduleすると、プロジェクトに別のgitリポジトリを含めることができます。

ユースケースに適用:

  1. shared-git-repositoryの現在のバージョンをプッシュする
  2. git submodule add <shared-git-repository-link>メインプロジェクト内で使用して、共有リポジトリをリンクします。

ドキュメントへのリンクは次のとおりです。https//git-scm.com/docs/git-submodule


これは実際に悪い考えではありませんが、ローカルでの開発とテストは基本的にこのアプローチではなくなります。
MauriceNino

0

私があなたの問題を正しく理解している場合、解決策は単一の回答よりも複雑であり、それはあなたの好みに一部依存します。

アプローチ1:ローカルコピー

Gulpを使用して、すでに説明した作業ソリューションを自動化できますが、IMOは保守が非常に簡単ではなく、ある時点で別の開発者が参加すると、複雑さが大幅に増大します。

アプローチ2:Monorepo

3つのフォルダすべてを含む単一のリポジトリを作成し、それらを接続して、単一のプロジェクトとして動作するようにすることができます。上記ですでに回答したように、Lernaを使用できます。少し設定が必要ですが、一度完了すると、これらのフォルダーは単一のプロジェクトとして動作します。

アプローチ3:コンポーネント

これらのフォルダーをそれぞれスタンドアロンコンポーネントとして扱います。ビットを見てください。これにより、大きなプロジェクトの小さな部分としてフォルダーを設定でき、それらのコンポーネントを自分だけにスコープするプライベートアカウントを作成できます。最初に設定すると、個別のフォルダーに更新を適用することもでき、それらを使用する親フォルダーは自動的に更新を取得します。

アプローチ4:パッケージ

npmを使用したくないと具体的に言ったが、私はそれを共有したい。なぜなら、私は現在、以下で説明するセットアップを使用しており、私にとって完璧な仕事をしているからです。

  1. npmまたはyarnを使用して、各フォルダーのパッケージを作成します(両方のスコープパッケージを作成して、コードが利用できるようにすることができます)。
  2. 親フォルダー(これらのフォルダーをすべて使用する)では、作成されたパッケージが依存関係として接続されます。
  3. 私はすべてのコードをバンドルするためにwebpackを使用し、typescriptパスと組み合わせてwebpackパスエイリアスを使用しています。

チャームのように動作し、パッケージがローカル開発用にシンボリックリンクされている場合、それは完全にオフラインで動作し、私の経験では-各フォルダーは個別にスケーラブルで、メンテナンスが非常に簡単です。

注意

「子」パッケージはかなり大きいため、すでにプリコンパイルされています。パッケージごとに個別のtsconfigを作成しましたが、簡単に変更できるというのがすばらしい点です。過去には、モジュールとコンパイル済みファイルの両方でtypescriptを使用し、未加工のjsファイルも使用していたため、全体が非常に用途が広いです。

お役に立てれば

***** UPDATE ****ポイント4を続けるために、申し訳ありません。私の知る限り、アップロードされていないモジュールはシンボリックリンクできないため、私はそれを誤解したのかもしれません。それにもかかわらず、ここにあります:

  1. 別のnpmモジュールがあります。使用しましょう firebase-functionsそれをて。好みに応じて、コンパイルするか、生のtsを使用します。
  2. 親プロジェクトに追加 firebase-functions依存関係としてし。
  3. tsconfig.json、アドオン"paths": {"firebase-functions: ['node_modules/firebase-functions']"}
  4. Webpack内- resolve: {extensions: ['ts', 'js'], alias: 'firebase-functions': }

このようにして、firebase-functions単にを使用して、モジュールからエクスポートされたすべての関数を参照しますimport { Something } from 'firebase-functions'。WebpackとTypeScriptは、それをノードモジュールフォルダーにリンクします。この構成では、親プロジェクトは、firebase-functionsモジュールがTypeScriptまたはバニラJavaScriptで記述されて。

設定すると、本番環境で完全に機能します。次に、リンクしてオフラインで作業するには:

  1. 移動firebase-functionsプロジェクトと書き込みnpm link。マシンにローカルなシンボリックリンクを作成し、package.jsonで設定した名前にリンクをマッピングします。
  2. 親プロジェクトに移動してを書き込みますnpm link firebase-functions。これにより、シンボリックリンクが作成され、firebase-functionsの依存関係が、作成したフォルダーにマッピングされます。

あなたは何かを誤解したと思います。npmを使いたくないと言ったことはありません。実際、これら3つのモジュールはすべてノードモジュールです。モジュールをnpmにアップロードしたくないと言っただけです。その4番目の部分についてもう少し詳しく説明してもらえますか?多分コードサンプルを提供しますか?
MauriceNino

コメントとして長くて読み
づらい

私の最初の回答を更新しました。より明確になることを願っています
Ivan Dzhurov

0

ローカルで使用するためにコードをnpmにアップロードしたくないので、コードをアップロードする予定はありません。100%オフラインで動作するはずです。

すべてのnpmモジュールはローカルにインストールされ、常にオフラインで動作しますが、パッケージを公開して他の人に見られたくない場合は、プライベートnpmレジストリをインストールできます。

ProGetはNuGet / Npmプライベートリポジトリサーバーで、プライベートパッケージをホスト、アクセス、公開するためにプライベート開発/運用環境で使用できるWindowsで使用できます。それはWindows上ですが、Linux上で利用可能なさまざまな代替手段があると確信しています。

  1. Gitサブモジュールは悪い考えです。これは、パッケージのようにバージョン管理されていないコードを共有する古い方法であり、サブモジュールの変更とコミットは非常に困難です。
  2. ソースインポートフォルダーも悪い考えです。バージョン管理が問題になります。誰かが依存リポジトリの依存フォルダーを変更した場合、再び追跡するのは悪夢だからです。
  3. npmはすでにパッケージを適切に管理するためのさまざまなツールを提供しているため、パッケージの分離をエミュレートするサードパーティのスクリプトツールは時間の無駄です。

これが私たちの構築/配備シナリオです。

  1. すべてのプライベートパッケージには、.npmrcが含まれていますregistry=https://private-npm-repository
  2. すべてのプライベートパッケージを、プライベートにホストされているProGetリポジトリに公開します。
  3. すべてのプライベートパッケージには、ProGetの依存プライベートパッケージが含まれています。
  4. 私たちのビルドサーバーは、私たちが設定したnpm認証を介してProGetにアクセスします。私たちのネットワーク外の誰もこのリポジトリにアクセスできません。
  5. ビルドサーバーは、bundled dependencies内部にすべてのパッケージを含むnpmパッケージを作成node_modulesします。必要なすべてのパッケージが既にバンドルされているため、運用サーバーはNPMまたはプライベートNPMパッケージにアクセスする必要がありません。

プライベートnpmリポジトリを使用すると、さまざまな利点があります。

  1. カスタムスクリプトは不要
  2. ノードのbuid / publishパイプラインに適合
  3. すべてのプライベートnpmパッケージには、プライベートgitソース管理への直接リンクが含まれ、将来のエラーのデバッグと調査が容易になります
  4. すべてのパッケージは読み取り専用のスナップショットであるため、一度公開すると変更できません。新しい機能を作成している間、古いバージョンの依存パッケージを含む既存のコードベースには影響しません。
  5. いくつかのパッケージを簡単に公開し、将来的に他のリポジトリに移動することができます
  6. プライベートnpmプロバイダーソフトウェアが変更された場合(たとえば、コードをノードのプライベートnpmパッケージレジストリクラウドに移動する場合)、コードを変更する必要はありません。

これは解決策かもしれませんが、残念ながら私には適していません。お時間をいただきありがとうございます!
MauriceNino、

小さなノード・サーバーとしてインストールされているローカルNPMリポジトリもあるverdaccio.orgは
Akashさんカバ

-1

あなたが探しているツールはnpm linkです。npm linkローカルnpmパッケージへのシンボリックリンクを提供します。これにより、npmパッケージライブラリにパッケージを公開することなく、パッケージをリンクしてメインプロジェクトで使用できます。

ユースケースに適用:

  1. パッケージnpm link内で使用しますshared。これにより、今後のインストールのためにシンボリックリンクの宛先が設定されます。
  2. メインプロジェクトに移動します。functionsパッケージ内で、を使用npm link sharedして共有パッケージをリンクし、node_modulesディレクトリに追加します。

ドキュメントへのリンクは次のとおりです。https//docs.npmjs.com/cli/link.html


私の知る限り、npmリンクはテスト専用であり、結果のコード(たとえば、関数)をデプロイする場合は機能しません。
MauriceNino

おそらく、この要件を質問に追加する必要があります。
フリードウ

すでに質問で触れられていますが、明確にしていきます。
MauriceNino
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.