目標
だから私はこの構造を持つプロジェクトを持っています:
目標は、shared
モジュールで共通のインターフェースとクラスを定義することです。
制限
ローカルで使用するためにコードをnpmにアップロードしたくないので、コードをアップロードする予定はありません。 100%オフラインで動作するはずです。
開発プロセスはオフラインで動作するはずですが、ionic-app
およびfirebase-functions
モジュールがfirebase(ホスティングおよび関数)にデプロイされます。したがって、shared
モジュールのコードがそこにあるはずです。
これまでに試したこと
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は、それを別のファイルとして扱います。
序文: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サービスをデプロイするときに、コマンドを predeployおよびpostdeployフック にアタッチできます。これは、目的のサービスのプロパティpredeploy
およびpostdeploy
を介してfirebase.json
で行われます。これらのプロパティには、コードをデプロイする前と後にそれぞれ実行する一連のコマンドの配列が含まれています。さらに、これらのコマンドは、環境変数RESOURCE_DIR
(./functions
または./ionic-app
のいずれか該当する方)およびPROJECT_DIR
(firebase.json
を含むディレクトリパス)で呼び出されます。
firebase.json
内のpredeploy
のfunctions
配列を使用して、共有ライブラリのコードをCloud Functionsインスタンスにデプロイされているフォルダーにコピーできます。これを行うことで、サブフォルダーにあるライブラリのように共有コードを単純に含めることができます。または、tsconfig.json
で TypeScriptのパスマッピング を使用して名前付きモジュールに名前をマップできます(したがって、import { hiThere } from 'shared';
を使用できます) )。
predeploy
フック定義(Windowsとの互換性のために shx
のグローバルインストールを使用):
// 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
}
同じ方法をホスティングフォルダーでも使用できます。
複数のパッケージを持つJavaScript(およびTypeScript)プロジェクトを管理するためのツールである Lerna を試してみるとよいでしょう。
プロジェクトが次のディレクトリ構造を持っていると仮定します。
packages
ionic-app
package.json
firebase-functions
package.json
shared
package.json
公開したくないすべてのモジュールと、private
モジュールのtypings
エントリで、正しいアクセスレベル(shared
およびconfig/access
キー)を指定してください。
共有:
{
"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"
}
}
上記の変更を行うと、ルートレベルpackage.json
を作成できます。ここで、ユニットテストフレームワーク、tslintなど、すべてのプロジェクトモジュールにアクセスさせたい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
を実行すると、ルートレベルpackage.json
で定義されているpostinstall
スクリプトが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
はモジュールの各package.json
ファイルでcompile
というスクリプトを呼び出します。
モジュールごとに独自のcompile
スクリプトがあるため、モジュールごとにtsonfig.json
ファイルを作成できます。複製が気に入らない場合は、ルートレベルのtsconfig、またはルートレベルのtsconfigファイルとモジュールレベルのtsconfigファイルの組み合わせをルートから継承することで回避できます。
この設定が実際のプロジェクトでどのように機能するかを確認したい場合は、- Serenity/JS を参照してください。
shared
モジュールをnode_modules
の下のfirebase-functions
とionic-app
の下でシンボリックリンクし、プロジェクトルートの下のdevDepedencies
をnode_modules
の下にシンボリックリンクすることの良い点は、コンシューマモジュールをデプロイする必要がある場合です。どこでも(たとえばionic-app
など)、node_modules
と一緒にすべてをZipするだけで、展開前に開発の依存関係を削除する必要がないか心配する必要はありません。
お役に立てれば!
ヤン
Gitを使用してコードを管理している場合のもう1つの解決策は、git submodule
を使用することです。 git submodule
を使用すると、プロジェクトに別のgitリポジトリを含めることができます。
ユースケースに適用:
git submodule add <shared-git-repository-link>
を使用して、共有リポジトリをリンクします。ドキュメントへのリンクは次のとおりです。 https://git-scm.com/docs/git-submodule
私があなたの問題を正しく理解している場合、解決策は単一の回答よりも複雑であり、それはあなたの好みに一部依存します。
アプローチ1:ローカルコピー
Gulp を使用して、すでに説明した作業ソリューションを自動化できますが、IMOは維持が非常に簡単ではなく、ある時点で別の開発者が参加すると、複雑さが大幅に増大します。
アプローチ2:Monorepo
3つのフォルダーすべてを含む単一のリポジトリーを作成し、それらを接続して、それらが単一のプロジェクトとして動作するようにすることができます。上記ですでに回答したように、 Lerna を使用できます。少し設定が必要ですが、一度完了すると、これらのフォルダーは単一のプロジェクトとして動作します。
アプローチ3:コンポーネント
これらのフォルダーのそれぞれをスタンドアロンコンポーネントとして扱います。 Bit を見てください。これにより、大きなプロジェクトの小さな部分としてフォルダーを設定でき、これらのコンポーネントのスコープを自分だけに限定するプライベートアカウントを作成できます。最初に設定すると、個別のフォルダーに更新を適用することもでき、それらを使用する親フォルダーは自動的に更新を取得します。
アプローチ4:パッケージ
Npmを使用したくないと具体的に言ったが、私はそれを共有したい。なぜなら、私は現在、以下で説明するセットアップで作業しており、私にとって完璧な仕事をしているからです。
npm
またはyarn
を使用して、各フォルダーのパッケージを作成します(これらの両方のスコープパッケージを作成して、コードが利用できる場合にのみ、コードを使用できます)。チャームのように動作し、パッケージがローカル開発用にシンボリックリンクされている場合、それは完全にオフラインで動作し、私の経験では-各フォルダーは個別にスケーラブルであり、メンテナンスが非常に簡単です。
注
「子」パッケージはかなり大きいため、すでにプリコンパイルされています。パッケージごとに個別のtsconfigを作成しましたが、簡単に変更できるというのがすばらしい点です。過去には、モジュールとコンパイルされたファイルでTypeScriptを使用しており、未加工のjsファイルも使用していたため、全体が非常に用途が広いです。
お役に立てれば
***** UPDATE ****ポイント4に進むために、申し訳ありません。私の知る限り、アップロードされていないモジュールはシンボリックリンクできないため、間違っているのかもしれません。それにもかかわらず、ここにあります:
firebase-functions
を使用してみましょう。好みに応じて、コンパイルするか、生のtsを使用します。firebase-functions
を追加します。tsconfig.json
に"paths": {"firebase-functions: ['node_modules/firebase-functions']"}
を追加resolve: {extensions: ['ts', 'js'], alias: 'firebase-functions': }
このように、firebase-functions
を使用するだけで、import { Something } from 'firebase-functions'
モジュールからエクスポートされたすべての関数を参照できます。 WebpackとTypeScriptは、それをノードモジュールフォルダーにリンクします。この構成では、親プロジェクトは、firebase-functions
モジュールがTypeScriptまたはVanilla JavaScriptで記述されているかどうかを気にしません。
設定すると、本番環境で完全に機能します。次に、リンクしてオフラインで作業するには:
firebase-functions
プロジェクトに移動し、npm link
と書き込みます。マシンにローカルなシンボリックリンクを作成し、package.jsonで設定した名前にリンクをマッピングします。npm link firebase-functions
を書き込みます。これにより、シンボリックリンクが作成され、firebase-functionsの依存関係が、作成したフォルダーにマッピングされます。ローカルで使用するためにコードをnpmにアップロードしたくないので、コードをアップロードする予定はありません。 100%オフラインで動作するはずです。
すべてのnpmモジュールはローカルにインストールされ、常にオフラインで動作しますが、パッケージを公開して他の人に見られたくない場合は、プライベートnpmレジストリをインストールできます。
ProGetは、Windows用のNuGet/Npmプライベートリポジトリサーバーであり、プライベート開発/本番環境で使用して、プライベートパッケージのホスト、アクセス、公開を行うことができます。それはWindows上ですが、Linux上で利用可能なさまざまな選択肢があると確信しています。
これが私たちのビルド/展開シナリオです。
.npmrc
を含むregistry=https://private-npm-repository
があります。bundled dependencies
内のすべてのパッケージを含むnode_modules
を使用してnpmパッケージを作成します。本番サーバーは、必要なすべてのパッケージがすでにバンドルされているため、NPMまたはプライベートNPMパッケージにアクセスする必要はありません。プライベートnpmリポジトリを使用すると、さまざまな利点があります