Menggunakan modul node bersama untuk kelas umum

15

Tujuan

Jadi saya memiliki proyek dengan struktur ini:

  • aplikasi ion
  • fungsi firebase
  • bersama

Tujuannya adalah untuk mendefinisikan antarmuka dan kelas umum dalam sharedmodul.

Batasan

Saya tidak ingin mengunggah kode saya ke npm untuk menggunakannya secara lokal dan saya tidak berencana untuk mengunggah kode sama sekali. Seharusnya 100% berfungsi offline.

Sementara proses pengembangan harus bekerja offline, ionic-appdan firebase-functionsmodul akan digunakan untuk firebase (hosting & fungsi). Oleh karena itu, kode dari sharedmodul harus tersedia di sana.

Apa yang saya coba sejauh ini

  • Saya telah mencoba menggunakan Referensi Proyek dalam naskah, tetapi saya belum membuatnya bekerja
  • Saya mencobanya dengan menginstalnya sebagai modul npm seperti pada jawaban kedua dari pertanyaan ini
    • Tampaknya bekerja dengan baik pada awalnya, tetapi selama membangun, saya mendapatkan kesalahan seperti ini ketika menjalankan 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)

Pertanyaan

Apakah Anda memiliki solusi untuk membuat modul yang dibagikan menggunakan konfigurasi huruf, atau NPM?

Tolong jangan tandai ini sebagai duplikat → Saya telah mencoba solusi apa pun yang saya temukan di StackOverflow.

Informasi tambahan

Konfigurasi untuk dibagikan:

// 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"
  }
}

Konfigurasi untuk fungsi:

// 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 saat ini

Saya telah menambahkan skrip npm ke modul bersama, yang menyalin semua file (tanpa index.js) ke modul lain. Ini memiliki masalah, bahwa saya memeriksa kode duplikat ke dalam SCM, dan bahwa saya perlu menjalankan perintah itu pada setiap perubahan. Juga, IDE hanya memperlakukannya sebagai file yang berbeda.

MauriceNino
sumber

Jawaban:

4

Pendahuluan: Saya tidak terlalu terbiasa dengan cara kerja kompilasi ScriptScript dan bagaimana package.jsondalam modul seperti itu harus didefinisikan. Solusi ini, meskipun berhasil, dapat dianggap sebagai cara yang sulit untuk mencapai tugas yang ada.

Dengan asumsi struktur direktori berikut:

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

Saat menggunakan layanan Firebase, Anda dapat melampirkan perintah ke kait prapengguna dan pascabayar . Ini dilakukan firebase.jsonmelalui properti predeploydan postdeploypada layanan yang diinginkan. Properti ini mengandung larik perintah berurutan untuk dijalankan sebelum dan sesudah menggunakan kode Anda masing-masing. Selanjutnya, perintah ini dipanggil dengan variabel lingkungan RESOURCE_DIR(jalur direktori ./functionsatau ./ionic-app, mana yang berlaku) dan PROJECT_DIR(jalur direktori berisi firebase.json).

Menggunakan predeployarray untuk functionsdi dalam firebase.json, kita bisa menyalin kode perpustakaan bersama ke folder yang digunakan untuk instance Cloud Functions. Dengan melakukan ini, Anda hanya dapat menyertakan kode bersama seolah-olah itu sebuah perpustakaan yang terletak di subfolder atau Anda dapat memetakan nama itu menggunakan pemetaan jalur Naskah yang diketik ini dalam tsconfig.jsonsebuah modul bernama (sehingga Anda dapat menggunakan import { hiThere } from 'shared';).

The predeploykait definition (penggunaan global yang menginstal shxuntuk kompatibilitas 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",
    ...
  }
}

Menautkan sumber naskah naskah yang disalin ke konfigurasi kompilasi naskah naskah fungsi:

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

Mengaitkan nama modul, "dibagikan", ke folder paket perpustakaan yang disalin.

// 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
}

Pendekatan yang sama dapat digunakan dengan folder hosting.


Semoga ini menginspirasi seseorang yang lebih akrab dengan kompilasi Typecript untuk datang dengan solusi bersih yang memanfaatkan kait ini.

samthecodingman
sumber
3

Anda mungkin ingin mencoba Lerna , alat untuk mengelola proyek JavaScript (dan TypeScript) dengan beberapa paket.

Mendirikan

Dengan asumsi bahwa proyek Anda memiliki struktur direktori berikut:

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

Pastikan untuk menentukan level akses ( privatedan config/accesskunci) yang benar di semua modul yang tidak ingin Anda terbitkan, serta typingsentri dalam sharedmodul Anda :

Bersama:

{
  "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"
  }
}

Aplikasi ionik:

{
  "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"
  }
}

Dengan perubahan di atas, Anda dapat membuat tingkat root di package.jsonmana Anda dapat menentukan apa saja devDependenciesyang Anda inginkan agar semua modul proyek Anda memiliki akses, seperti kerangka pengujian unit, tslint, dll.

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

Anda juga dapat menggunakan level root ini package.jsonuntuk mendefinisikan skrip npm yang akan memanggil skrip yang sesuai dalam modul proyek Anda (via lerna):

{
  "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"
  },
}

Dengan itu, tambahkan file konfigurasi lerna di direktori root Anda:

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

dengan konten berikut:

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

Sekarang ketika Anda menjalankan npm installdi direktori root, postinstallskrip yang ditentukan di tingkat root Anda package.jsonakan dipanggil lerna bootstrap.

Apa yang lerna bootstrapdilakukannya adalah itu akan menghubungkan sharedmodul Anda ke ionic-app/node_modules/shareddan firebase-functions/node_modules/shared, jadi dari sudut dua modul itu, sharedterlihat seperti modul npm lainnya.

Kompilasi

Tentu saja, menghubungkan modul tidak cukup karena Anda masih perlu mengompilasinya dari TypeScript ke JavaScript.

Di situlah package.json compileskrip level-root mulai berlaku.

Ketika Anda menjalankan npm run compiledi root proyek Anda, npm akan memanggil lerna run compile --stream, dan lerna run compile --streammemanggil skrip yang dipanggil compiledi setiap package.jsonfile modul Anda .

Karena setiap modul Anda sekarang memiliki compileskrip sendiri , Anda harus dapat memiliki tsonfig.jsonfile per modul. Jika Anda tidak menyukai duplikasi tersebut, Anda bisa lolos dengan tsconfig level root, atau kombinasi dari tsconfig level root dan file tsconfig level modul yang diwarisi dari root.

Jika Anda ingin melihat bagaimana pengaturan ini bekerja pada proyek dunia nyata, lihat Serenity / JS di mana saya telah menggunakannya secara ekstensif.

Penyebaran

Hal yang menyenangkan tentang memiliki sharedmodul yang disinkronkan di node_modulesbawah firebase-functionsdan ionic-app, dan devDepedenciesdi node_modulesbawah proyek Anda di bawah root adalah bahwa jika Anda perlu menggunakan modul konsumen di mana saja (jadi ionic-appmisalnya), Anda bisa mengaitkannya bersama-sama dengan modulnya node_modulesdan tidak khawatir tentang harus menghapus dev dev sebelum penyebaran.

Semoga ini membantu!

Jan

Jan Molak
sumber
Intresting! Saya pasti akan memeriksanya dan melihat apakah ini cocok.
MauriceNino
2

Solusi lain yang mungkin, jika Anda menggunakan git untuk mengelola kode Anda, adalah menggunakan git submodule. Menggunakan git submoduleAnda dapat memasukkan repositori git lain ke proyek Anda.

Diterapkan ke kasus penggunaan Anda:

  1. Dorong versi repositori shared-git Anda saat ini
  2. Gunakan git submodule add <shared-git-repository-link>di dalam proyek utama Anda untuk menautkan repositori bersama.

Berikut ini tautan ke dokumentasi: https://git-scm.com/docs/git-submodule

friedow
sumber
Ini sebenarnya bukan ide yang buruk, tetapi pengembangan dan pengujian lokal pada dasarnya berjalan dengan pendekatan ini.
MauriceNino
0

Jika saya memahami masalah Anda dengan benar, solusinya lebih kompleks daripada satu jawaban dan sebagian tergantung pada preferensi Anda.

Pendekatan 1: Salinan lokal

Anda dapat menggunakan Gulp untuk mengotomatiskan solusi kerja yang telah Anda jelaskan, tetapi IMO itu tidak mudah untuk mempertahankan dan secara drastis meningkatkan kompleksitas jika pada suatu titik pengembang lain masuk.

Pendekatan 2: Monorepo

Anda dapat membuat repositori tunggal yang berisi ketiga folder dan menghubungkannya sehingga berperilaku sebagai satu proyek. Seperti yang sudah dijawab di atas, Anda dapat menggunakan Lerna . Ini memerlukan sedikit konfigurasi, tetapi setelah selesai, folder-folder itu akan berperilaku sebagai satu proyek.

Pendekatan 3: Komponen

Perlakukan masing-masing folder ini sebagai komponen yang berdiri sendiri. Lihatlah Bit . Ini akan memungkinkan Anda untuk mengatur folder sebagai bagian yang lebih kecil dari proyek yang lebih besar dan Anda dapat membuat akun pribadi yang akan lingkup komponen hanya untuk Anda. Setelah awalnya diatur, itu akan memungkinkan Anda untuk bahkan menerapkan pembaruan ke folder yang terpisah dan orang tua yang menggunakannya akan secara otomatis mendapatkan pembaruan.

Pendekatan 4: Paket

Anda secara khusus mengatakan bahwa Anda tidak ingin menggunakan npm, tetapi saya ingin membagikannya, karena saya saat ini bekerja dengan pengaturan seperti yang dijelaskan di bawah ini dan sedang melakukan pekerjaan yang sempurna untuk saya:

  1. Gunakan npmatau yarnuntuk membuat paket untuk setiap folder (Anda dapat membuat paket scoped untuk keduanya sehingga kode hanya akan tersedia untuk Anda, jika ini menjadi perhatian Anda).
  2. Di folder induk (yang menggunakan semua folder ini), paket yang dibuat terhubung sebagai dependensi.
  3. Saya menggunakan webpack untuk menggabungkan semua kode, menggunakan alias jalur webpack dalam kombinasi dengan jalur naskah.

Bekerja seperti pesona dan ketika paket di-symlink untuk pengembangan lokal, ia bekerja sepenuhnya offline dan menurut pengalaman saya - setiap folder dapat diskalakan secara terpisah dan sangat mudah untuk dipelihara.

Catatan

Paket 'child' sudah dikompilasi dalam kasus saya karena mereka cukup besar dan saya telah membuat tsconfigs terpisah untuk setiap paket, tetapi hal yang indah adalah Anda dapat mengubahnya dengan mudah. Di masa lalu saya berdua menggunakan naskah dalam modul dan file yang dikompilasi, dan juga file js mentah, sehingga semuanya sangat, sangat fleksibel.

Semoga ini membantu

***** UPDATE **** Untuk melanjutkan pada poin 4: Saya minta maaf, saya salah. Mungkin saya salah karena sejauh yang saya tahu, Anda tidak dapat menyinkronkan modul jika tidak diunggah. Namun demikian, ini dia:

  1. Anda memiliki modul npm yang terpisah, mari kita gunakan firebase-functionsuntuk itu. Anda mengkompilasinya, atau menggunakan ts mentah, tergantung pada preferensi Anda.
  2. Dalam proyek induk Anda, tambahkan firebase-functionssebagai ketergantungan.
  3. Dalam tsconfig.json, tambahkan"paths": {"firebase-functions: ['node_modules/firebase-functions']"}
  4. Di webpack - resolve: {extensions: ['ts', 'js'], alias: 'firebase-functions': }

Dengan cara ini, Anda merujuk semua fungsi yang diekspor dari firebase-functionsmodul hanya dengan menggunakan import { Something } from 'firebase-functions'. Webpack dan TypeScript akan menautkannya ke folder modul simpul. Dengan konfigurasi ini, proyek induk tidak akan peduli jika firebase-functionsmodul ditulis dalam TypeScript atau vanilla javascript.

Setelah diatur, ini akan bekerja dengan sempurna untuk produksi. Lalu, untuk menautkan dan bekerja offline:

  1. Arahkan ke firebase-functionsproyek dan tulis npm link. Ini akan membuat symlink, lokal ke mesin Anda dan akan memetakan tautan nama yang Anda atur di package.json.
  2. Arahkan ke proyek induk dan tulis npm link firebase-functions, yang akan membuat symlink dan memetakan ketergantungan fungsi-fungsi firebase ke folder di mana Anda telah membuatnya.
Ivan Dzhurov
sumber
Saya pikir Anda salah mengerti sesuatu. Saya tidak pernah mengatakan saya tidak ingin menggunakan npm. Sebenarnya ketiga modul tersebut adalah modul simpul. Saya hanya mengatakan, bahwa saya tidak ingin mengunggah modul saya ke npm. Bisakah Anda menguraikan bagian 4 itu sedikit lebih - yang terdengar menarik? mungkin memberikan contoh kode?
MauriceNino
Saya akan menambahkan jawaban lain, karena akan panjang dan tidak dapat dibaca sebagai komentar
Ivan Dzhurov
Memperbarui jawaban awal saya, semoga lebih jelas
Ivan Dzhurov
0

Saya tidak ingin mengunggah kode saya ke npm untuk menggunakannya secara lokal dan saya tidak berencana untuk mengunggah kode sama sekali. Seharusnya 100% berfungsi offline.

Semua modul npm diinstal secara lokal dan selalu bekerja offline, tetapi jika Anda tidak ingin mempublikasikan paket Anda secara publik sehingga orang dapat melihatnya, Anda dapat menginstal registry npm pribadi.

ProGet adalah server repositori NuGet / Npm Private yang tersedia untuk windows yang dapat Anda gunakan dalam pengembangan pribadi / lingkungan produksi Anda untuk menjadi tuan rumah, mengakses dan mempublikasikan paket pribadi Anda. Meskipun ada di windows tapi saya yakin ada berbagai alternatif yang tersedia di linux.

  1. Git Submodules adalah ide yang buruk, itu benar-benar cara kuno untuk berbagi kode yang tidak versi seperti paket, mengubah dan melakukan submodule adalah hal yang sangat menyakitkan.
  2. Folder impor sumber juga merupakan ide yang buruk, lagi versi adalah masalah, karena jika seseorang memodifikasi folder dependen dalam repositori dependen, lagi melacak itu adalah mimpi buruk.
  3. Setiap alat skrip pihak ketiga untuk meniru pemisahan paket adalah buang-buang waktu karena npm sudah menyediakan berbagai alat untuk mengelola paket dengan baik.

Berikut ini adalah skenario build / deployment kami.

  1. Setiap paket pribadi memiliki .npmrcyang berisi registry=https://private-npm-repository.
  2. Kami menerbitkan semua paket pribadi kami ke repositori ProGet kami yang dihosting secara pribadi.
  3. Setiap paket pribadi berisi paket pribadi yang tergantung pada ProGet.
  4. Server build kami mengakses ProGet melalui otentikasi npm yang ditetapkan oleh kami. Tidak ada orang di luar jaringan kami yang memiliki akses ke repositori ini.
  5. Server build kami menciptakan paket npm bundled dependenciesyang berisi semua paket di dalamnya node_modulesdan server produksi tidak perlu mengakses paket NPM atau NPM pribadi karena semua paket yang diperlukan sudah dibundel.

Menggunakan repositori npm pribadi memiliki berbagai keuntungan,

  1. Tidak perlu skrip khusus
  2. Sesuai dengan simpul buid / publikasikan pipa
  3. Setiap paket npm pribadi akan berisi tautan langsung ke kontrol sumber git pribadi Anda, mudah di-debug dan menyelidiki kesalahan di masa mendatang
  4. Setiap paket adalah snapshot hanya baca, jadi setelah dipublikasikan tidak dapat dimodifikasi, dan saat Anda membuat fitur baru, basis kode yang ada dengan versi yang lebih lama dari paket dependen tidak akan terpengaruh.
  5. Anda dapat dengan mudah membuat beberapa paket menjadi publik dan pindah ke beberapa repositori lain di masa depan
  6. Jika perangkat lunak penyedia npm pribadi Anda berubah, misalnya Anda memutuskan untuk memindahkan kode Anda ke cloud registry paket npm pribadi, Anda tidak perlu melakukan perubahan apa pun ke dalam kode Anda.
Akash Kava
sumber
Ini mungkin solusi, tapi sayangnya bukan untuk saya. Terima kasih atas waktu Anda!
MauriceNino
Ada juga repositori npm lokal yang diinstal sebagai server node kecil, verdaccio.org
Akash Kava
-1

Alat yang Anda cari adalah npm link. npm linkmenyediakan symlink ke paket npm lokal. Dengan begitu Anda dapat menautkan sebuah paket dan menggunakannya dalam proyek utama Anda tanpa menerbitkannya ke pustaka paket npm.

Diterapkan ke kasus penggunaan Anda:

  1. Gunakan npm linkdi dalam sharedpaket Anda . Ini akan menetapkan tujuan symlink untuk pemasangan di masa mendatang.
  2. Arahkan ke proyek utama Anda. Di dalam functionspaket Anda dan gunakan npm link shareduntuk menautkan paket bersama dan menambahkannya ke node_modulesdirektori.

Berikut ini tautan ke dokumentasi: https://docs.npmjs.com/cli/link.html

friedow
sumber
Sejauh yang saya tahu, tautan npm hanya untuk pengujian dan tidak berfungsi jika Anda ingin menggunakan kode yang dihasilkan (misalnya fungsi saya).
MauriceNino
Saya mengerti, Anda mungkin harus menambahkan persyaratan ini ke pertanyaan Anda.
friedow
Sudah disebutkan dalam pertanyaan, tapi saya akan mengklarifikasi.
MauriceNino