Firebase-tools: دعم mono-repos في النشر

تم إنشاؤها على ٣١ يناير ٢٠١٨  ·  47تعليقات  ·  مصدر: firebase/firebase-tools

انظر: https://github.com/firebase/firebase-functions/issues/172

معلومات الإصدار

3.17.3

خطوات التكاثر

سلوك متوقع

السلوك الفعلي

feature request

التعليق الأكثر فائدة

+1 على هذا ، ستحتاج وظائف السحابة عادةً إلى مشاركة بعض الأكواد الشائعة (مثل الواجهات) مع تطبيقات أخرى ، ومن الطرق الرائعة للتعامل مع هذا الأمر monorepo (مثل lerna) أو استخدام الروابط الرمزية مباشرةً. أخذت الأخير وحللت عن طريق إنشاء بعض البرامج النصية. المفهوم سهل للغاية: أنسخ ما هو مطلوب داخل دليل الوظائف وأزيله بعد ذلك

إليك كيف فعلت ذلك باستخدام بنية الدليل هذه:
""

  • جذر/
    | - .firebaserc
    | - firebase.json
    | - ...
  • المهام/
    | - src /
    | - package.json
    | - pre-publish.js
    | - post-publish.js
    | - ....
  • مشتركة /
    | - src /
    | - package.json
    | - ....
content of `pre-deploy.js`

const fs = تتطلب ("fs-extra") ؛

const packageJsonPath = "./package.json" ؛
const packageJson = تتطلب (packageJsonPath) ؛

(غير متزامن () => {
انتظار fs.remove ( ./shared
انتظار fs.copy ( ../shared ، ./shared

packageJson.dependencies["@project/shared"] = "file:./shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

}) () ؛

content of `post-deploy.js`

const packageJsonPath = "./package.json" ؛
const packageJson = تتطلب (packageJsonPath) ؛
const fs = تتطلب ("fs-extra") ؛

(غير متزامن () => {
انتظار fs.remove ( ./shared

packageJson.dependencies["@project/shared"] = "file:../shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

}) () ؛

Then update `firebase.json` like this (add the build script if you need, I build before in my pipeline)

"المهام": {
"المصدر": "الوظائف"،
"النشر المسبق": [
"npm --prefix" $ RESOURCE_DIR "تشغيل النشر المسبق"
] ،
"postdeploy": [
"npm --prefix" $ RESOURCE_DIR "تشغيل ما بعد النشر"
]
} ،
""

إذا قمت بالتصميم ، داخل الدليل dist أو lib يجب أن يكون لديك الآن شقيقان: وظائف ومشتركة (حدث هذا بسبب التبعية المشتركة). تأكد من تحديث الوظائف package.json main للإشارة إلى lib/functions/src/index.js لإنجاز عملية النشر.

في الوقت الحالي تم حلها ولكن هذا حل بديل وليس حلاً. أعتقد أن أدوات Firebase يجب أن تدعم بالفعل الروابط الرمزية

ال 47 كومينتر

يبدو أن هذا يعمل في الإعداد الخاص بي ، على سبيل المثال ، يلتقط deploy الحزم من الجذر node_modules ، على الرغم من أن package.json يقع ضمن مساحة العمل api/ ( لقد استخدمت اسم مجلد مختلفًا بدلاً من functions/ ). هل هناك أي شيء آخر يحتاج إلى إصلاح هنا؟

تحرير: علاوة على ذلك ، أنسخ package.json إلى api/dist ليتم استخدامه بواسطة deploy .

// firebase.json
  ...
  "functions": {
    "source": "api/dist"
  },
  ...

لذلك ، لا يزال مستويان من التداخل يحلان الجذر node_modules بنجاح.

dinvlad هل يمكنك مشاركة الريبو؟

orouz للأسف ليس بعد ، فهو مغلق المصدر في الوقت الحالي.

هل تمكن أي شخص من معالجة هذه المشكلة؟ قد تكون مشاركة مشروع مثال بسيط مفيدة للغاية.

audkar حاليًا أنا فقط أستخدم lerna.js.org وهو الأمر run لتنفيذ برنامج نصي npm في كل مجلد فرعي بهيكل المجلد هذا:

- service1/
|  - .firebaserc
|  - firebase.json
- service2/
|  - .firebaserc
|  - firebase.json
- app1/
|  - .firebaserc
|  - firebase.json
- app2/
|  - .firebaserc
|  - firebase.json
- firestore/
|  - firestore.rules
|  - firestore.indexes.json
- etc...

التأكد من أن ملفات firebase.json لكل خدمة لا تتداخل مع بعضها البعض متروك للمستخدم. تعني الاصطلاحات البسيطة لاستخدام مجموعات الوظائف واستهداف أسماء المواقع المتعددة أن هذا قد تم حله للوظائف السحابية والاستضافة. لم تحصل على حل لقواعد Firestore / GCS حتى الآن ، على الرغم من أن تقسيمها قد لا يكون مثاليًا ...

تمت مناقشته هنا مسبقًا - https://github.com/firebase/firebase-tools/issues/1116

jthegedus شكرا لك على ردك. لكني أعتقد أن قضية هذه التذكرة مختلفة. أحاول استخدام مساحات عمل الغزل. ويبدو أن أدوات Firebase لا تلتقط تبعيات الارتباط الرمزي عند تحميل الوظائف

آه ، عادل بما فيه الكفاية ، لقد تجنبت حفرة الأرانب بنفسي

هل يمكنك توضيح ما هي المشكلة؟ كما هو مذكور أعلاه ، أنا فقط أستخدم bare Yarn مع مساحات عمل api و app فيها ، وقمت ببنائها باستخدام yarn workspace api build && yarn workspace app build (مع نص build خاص بكل منها مساحة العمل). سكربتات البناء
1) تجميع رمز TS مع outDir إلى api/dist و app/dist على التوالي
2) انسخ ملفات package.json إلى مجلدات dist
3) انسخ yarn.lock من المجلد _root_ إلى مجلدات dist

بعد ذلك ، قمت بتشغيل yarn firebase deploy من المجلد _root_ ، وهو يلتقط كلاً من api/dist و app/dist بدون أي عوائق. يبدو أن firebase.json

  "functions": {
    "source": "api/dist"
  },
  "hosting": {
    "public": "app/dist",

لسوء الحظ ، ما زلت لا أستطيع مشاركة الكود بالكامل ، لكن هذا الإعداد هو كل ما يهم ، afaik.

قد أكون مخطئًا أيضًا ، لكنني أعتقد أن البرنامج النصي firebase deploy لا يستخدم فعليًا دليلك node_modules . أعتقد أنه يلتقط الكود ، package.json ، و yarn.lock من الدلائل dist ، ويقوم بالباقي.

هذا صحيح. القيمة الافتراضية لـ "jobs.ignore" في firebase.json هي
["node_modules"] لذلك لم يتم تحميلها. أعتقد أنه يمكنك تجاوز ذلك
على الرغم من أنك تريد شحن بعض الوحدات المحلية.

يوم الاثنين ، 17 يونيو ، 2019 ، 6:58 مساءً Denis Loginov [email protected]
كتب:

أيضًا ، قد أكون مخطئًا ولكني أعتقد أن برنامج نشر Firebase لا يفعل ذلك
في الواقع استخدام دليل node_modules الخاص بك. أعتقد أنه يلتقط فقط ملف
cod ، و package.json ، و yarn.lock من دلائل dist ، ويقوم بتنفيذ الامتداد
استراحة.

-
أنت تتلقى هذا لأنك مشترك في هذا الموضوع.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/firebase/firebase-tools/issues/653؟email_source=notifications&email_token=ACATB2U73VS2KIILUVRFFB3P3A6NPA5CNFSM4EOR24GKYY3PNVWWK3TUL52HS4DFVREXG43
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/ACATB2U3Q2TLVBICRJ3B5OLP3A6NPANCNFSM4EOR24GA
.

dinvlad نعم ، يتطلب الأمر package.json وأي ملف قفل تستخدمه أثناء قيامه بتثبيت deps في النشر السحابي بعد النشر.

أعتقد أن السيناريو الذي تم تحديده في الأصل في المشكلة الأخرى كان يستخدم حزمة مشتركة داخل مساحة العمل وبعض المشكلات المتعلقة برفع النطاق. نظرًا لأنني لم أكن أستخدم الغزل بهذه الطريقة ، يمكنني فقط التكهن بما قرأته هناك.

samtsternjthegedus شكرا، من الجيد أن نعرف!

يبدو أننا نتحدث جميعًا عن مشاكل مختلفة. سأحاول وصف مشكلة yarn workspaces .

مشروع معقد

تخطيط المشروع

- utilities/
|  - package.json
- functions/
|  - package.json
- package.json

_./package.json_

{
  "private": true,
  "workspaces": ["functions", "utilities"]
}

_functions / package.json_

{
  <...>
  "dependencies": {
    "utilities": "1.0.0",
    <...>
  }
}

مشكلة

خطأ أثناء نشر الوظيفة:

Deployment error.
Build failed: {"error": {"canonicalCode": "INVALID_ARGUMENT", "errorMessage": "`gen_package_lock` had stderr output:\nnpm WARN deprecated [email protected]: use String.prototype.padStart()\nnpm ERR! code E404\nnpm ERR! 404 Not Found: [email protected]\n\nnpm ERR! A complete log of this run can be found in:\nnpm ERR!     /builder/home/.npm/_logs/2019-06-18T07_10_42_472Z-debug.log\n\nerror: `gen_package_lock` returned code: 1", "errorType": "InternalError", "errorId": "1971BEF9"}}

تعمل الوظائف بشكل جيد محليًا على المحاكي.

حاولت الحلول

تحميل node_modules (باستخدام functions.ignore في _firebase.json_). النتيجة هي نفسها.

تخميني أن يكون ذلك بسبب utilities يتم إنشاء كما syslink في _node-modules_ node_modules/utilities -> ../../utilities

هل يمكن أن تكون أدوات Firebase لا تتضمن محتوى الوحدات النمطية المرتبطة بالرمز عند التحميل (لا يوجد إلغاء مرجعية)؟

عذرًا ، هل يمكنك توضيح المجلد الذي يوجد فيه firebase.json (وإظهار قسم التكوين الخاص به لـ functions

_firebase.json_ كان في المجلد الجذر. كان التكوين قياسيًا. سمث مثل هذا:

  "functions": {
    "predeploy": [
      "yarn --cwd \"$RESOURCE_DIR\" run lint",
      "yarn --cwd \"$RESOURCE_DIR\" run build"
    ],
    "source": "functions",
    "ignore": []
  },
  <...>

تم نشر كل شيء كما هو متوقع (بما في ذلك _node_modules_) باستثناء node_modules/utilities وهو رابط رمزي.


تمكنت من حل هذه المشكلة عن طريق كتابة بعض البرامج النصية التي:

  • إنشاء حزم لكل مساحة عمل ( yarn pack ). على سبيل المثال ، يؤدي هذا إلى إنشاء _utilities.tgz_.
  • نقل كل المخرجات إلى دير معين.
  • تعديل _package.json_ لاستخدام ملفات tgz لتبعيات مساحة العمل. على سبيل المثال ، dependencies { "utilities": "1.0.0" -> dependencies { "utilities": "file:./utilities.tgz"
  • نشر هذا دير لقاعدة النار

إخراج المحتوى دير قبل التحميل:

- dist
|  - lib
|  | -index.js
|  - utilities.tgz
|  - package.json <---------- This is modified to use *.tgz for workspaces

audkar اليوم واجهت نفس المشكلة مثلك.

أنا جديد في مساحات عمل Lerna و Yarn. كما أفهمها ، يمكنك أيضًا استخدام Lerna. هل سيساعد ذلك بأي شكل من الأشكال؟

الحل الخاص بك يبدو معقدًا بعض الشيء بالنسبة لي 🤔

تتساءل أيضًا ، ما المقصود بـ "--cwd" $ RESOURCE_DIR "؟

--cwd إلى "دليل العمل الحالي" و $RESOURCE_DIR يحمل قيمة لـ dir المصدر ( functions في هذه الحالة). ستؤدي إضافة هذا العلم إلى تنفيذ yarn في functions dir بدلاً من الجذر

audkar آه فهمت. لذلك يمكنك أن تفعل الشيء نفسه مع yarn workspace functions lint و yarn workspace functions build

dinvlad ليس من الواضح بالنسبة لي سبب استهداف مجلد dist ونسخ الأشياء هناك. إذا كنت تقوم بالبناء للتوزيع ، لكنك تركت package.json في مكانها وأشر إلى dist/index.js فحينئذٍ يجب أن تعمل الأشياء بنفس الطريقة؟ يجب عليك بعد ذلك ضبط المصدر على api بدلاً من api / dist.

dinvlad لقد تعلمت الأمر yarn workspace من تعليقاتك ، لكن لا يبدو أنها تجعله يعمل لسبب ما. انظر هنا . اي فكرة؟

آسف للذهاب قليلا خارج الموضوع هنا. ربما التعليق في SO ، لتقليل الضوضاء.

@ 0x80 أنسخ package.json إلى api/dist وأشر firebase.json إلى api/dist لذلك يتم فقط تجميع الملفات "المبنية" داخل وظيفة السحابة. لست متأكدًا مما سيحدث إذا أشرنا إلى firebase.json إلى api - ربما سيظل ذكيًا بما يكفي لتعبئة ما بداخل api/dist (على أساس main سمة package.json ). لكنني اعتقدت أنه من الأنظف أن أشير إلى api/dist .

Re yarn workspace ، أجبت على SO ؛)

dinvlad سيجمع جذر ما تشير إليه ، ولكن يمكنك وضع كل ما لا تريد تضمينه في قائمة التجاهل firebase.json.

لقد استخدمت الآن حلاً مشابهًا لـaudkar.

{
  "functions": {
    "source": "packages/cloud-functions",
    "predeploy": ["./scripts/pre-deploy-cloud-functions"],
    "ignore": [
      "src",
      "node_modules"
    ]
  }
}

ثم يكون النص البرمجي للوظائف السحابية قبل النشر هو:

#!/usr/bin/env bash

set -e

yarn workspace @gemini/common lint
yarn workspace @gemini/common build

cd packages/common
yarn pack --filename gemini-common.tgz
mv gemini-common.tgz ../cloud-functions/
cd -

cp yarn.lock packages/cloud-functions/

yarn workspace @gemini/cloud-functions lint
yarn workspace @gemini/cloud-functions build

وتحتوي الحزم / وظائف السحابة على ملف gitignore إضافي:

yarn.lock
*.tgz

هذا ما نجح معي

- root/
|  - .firebaserc
|  - firebase.json
- packages/
  | - package1/
  | - functions/
    | - dist/
    | - src/
    | packages.json

وفي root/firebase.json :
""
{
"المهام": {
"النشر المسبق": "npm --prefix" $ RESOURCE_DIR "تشغيل الإصدار" ،
"المصدر": "الحزم / الوظائف"
}
}
""

kaminskypavel هل وظائفك على العبوات / العبوة 1 (أو بعض حزم الأخوة الأخرى)؟

@ 0x80 موجب.

أعتقد أن هناك شيئًا أساسيًا أسأت فهمه حول monorepos. افترضت أنه يمكنك مشاركة حزمة ونشر تطبيق باستخدام تلك الحزمة دون نشر الحزمة المشتركة فعليًا إلى NPM.

يبدو أن هذا غير ممكن ، لأن عمليات النشر مثل Firebase أو Now.sh ستقوم عادةً بتحميل الكود ثم في السحابة يتم التثبيت والبناء. هل انا صائب؟

kaminskypavel لقد جربت هنا

audkar هل تنشر الحزمة المشتركة الخاصة بك إلى NPM ، أم أنك مثلي تحاول النشر باستخدام كود مشترك لم يتم نشره؟

@ 0x80 أنا معك على هذا الفهم - أعتقد أن عمليات نشر Firebase Function هي (خطأ) فقط على افتراض أن جميع الحزم المسماة في package.json ستكون متاحة على npm ، باسم تسريع عمليات النشر.

نظرًا لأن إعدادات مساحة عمل الغزل أصبحت أكثر شيوعًا ، أتخيل أن المزيد من الأشخاص سيتفاجأون من عدم تمكنهم من استخدام الحزم المرتبطة في وظائف Firebase - خاصةً لأنها تعمل بشكل جيد حتى يتم النشر.

مع إضافة npm لدعم مساحات العمل ، لدينا معيار نظام بيئي لكيفية عمل الحزم المحلية.

نظرًا لأن هذه المشكلة مضى عليها أكثر من عام ، فهل هناك أي تحديث من جانب Firebase بشأن الخطط (أو عدم وجود خطط) هنا؟

أعتقد أنها فرصة رائعة - مجموعة خدمات Firebase تتطلب إعدادًا أحاديًا جيدًا.

+1 على هذا ، ستحتاج وظائف السحابة عادةً إلى مشاركة بعض الأكواد الشائعة (مثل الواجهات) مع تطبيقات أخرى ، ومن الطرق الرائعة للتعامل مع هذا الأمر monorepo (مثل lerna) أو استخدام الروابط الرمزية مباشرةً. أخذت الأخير وحللت عن طريق إنشاء بعض البرامج النصية. المفهوم سهل للغاية: أنسخ ما هو مطلوب داخل دليل الوظائف وأزيله بعد ذلك

إليك كيف فعلت ذلك باستخدام بنية الدليل هذه:
""

  • جذر/
    | - .firebaserc
    | - firebase.json
    | - ...
  • المهام/
    | - src /
    | - package.json
    | - pre-publish.js
    | - post-publish.js
    | - ....
  • مشتركة /
    | - src /
    | - package.json
    | - ....
content of `pre-deploy.js`

const fs = تتطلب ("fs-extra") ؛

const packageJsonPath = "./package.json" ؛
const packageJson = تتطلب (packageJsonPath) ؛

(غير متزامن () => {
انتظار fs.remove ( ./shared
انتظار fs.copy ( ../shared ، ./shared

packageJson.dependencies["@project/shared"] = "file:./shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

}) () ؛

content of `post-deploy.js`

const packageJsonPath = "./package.json" ؛
const packageJson = تتطلب (packageJsonPath) ؛
const fs = تتطلب ("fs-extra") ؛

(غير متزامن () => {
انتظار fs.remove ( ./shared

packageJson.dependencies["@project/shared"] = "file:../shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

}) () ؛

Then update `firebase.json` like this (add the build script if you need, I build before in my pipeline)

"المهام": {
"المصدر": "الوظائف"،
"النشر المسبق": [
"npm --prefix" $ RESOURCE_DIR "تشغيل النشر المسبق"
] ،
"postdeploy": [
"npm --prefix" $ RESOURCE_DIR "تشغيل ما بعد النشر"
]
} ،
""

إذا قمت بالتصميم ، داخل الدليل dist أو lib يجب أن يكون لديك الآن شقيقان: وظائف ومشتركة (حدث هذا بسبب التبعية المشتركة). تأكد من تحديث الوظائف package.json main للإشارة إلى lib/functions/src/index.js لإنجاز عملية النشر.

في الوقت الحالي تم حلها ولكن هذا حل بديل وليس حلاً. أعتقد أن أدوات Firebase يجب أن تدعم بالفعل الروابط الرمزية

michelepatrassi مستوحى مما ذكرته لي ، لقد أنشأت مكتبة firelink لإدارة هذه الحالة. يستخدم داخليًا rsync لنسخ الملفات العودية.

https://github.com/rxdi/firelink

npm i -g @rxdi/firelink

الاستخدام الأساسي
بافتراض أن لديك نهج monorepo وأن الحزم الخاصة بك تقع على مستويين لأسفل من الدليل الحالي حيث يوجد package.json .

package.json

  "fireDependencies": {
    "@graphql/database": "../../packages/database",
    "@graphql/shared": "../../packages/shared",
    "@graphql/introspection": "../../packages/introspection"
  },

تنفيذ firelink سيتم نسخ الحزم ذات الصلة بالمجلدات ثم تعيين الحزم الموجودة مع تثبيت الوحدة المحلية "@graphql/database": "file:./.packages/database", ثم تنفيذ الأمر firebase وسيقوم بتمرير باقي الوسائط من firelink الأمر
أساسًا firelink هو بديل لـ firebase CLI لأنه يولد firebase في النهاية عند الانتهاء من وظيفته في نسخ packages وتعديل package.json !

يعتبر!

لقد تعرضنا للعض من هذا ، أعتقد أن monorepos سيصبح المعيار ويجب دعم هذا افتراضيًا.

يا رفاق ، الحل البسيط لهذه المشكلة هو استخدام حزمة الويب لتجميع وظائف السحابة الخاصة بك ونشرها من الدليل المجمع. مرفق هنا ملف حزمة ويب أساسي أستخدمه. أثناء الإنشاء ، سيؤدي هذا إلى حزم جميع التعليمات البرمجية للوظائف ، بما في ذلك التبعيات التي تم حلها داخل mono-repo إلى مجلد المستوى الأعلى (في هذه الحالة webpack/cloud-functions ، يمكن أن يكون أي شيء تقوم بتكوينه)

const path = require('path');

module.exports = {
  target: 'node',
  mode: 'production',
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.json']
  },
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, '../../webpack/cloud-functions/dist'),
    libraryTarget: 'commonjs'
  },
  externals: {
    'firebase-admin': 'firebase-admin',
    'firebase-functions': 'firebase-functions'
  }
};

وأخيرًا في ملف firebase.json ، ارجع إلى هذا المجلد للنشر.

{
  "functions": {
    "source": "webpack/cloud-functions"
  }
}

تذكر أن يكون لديك ملف package.json في المجلد webpack/cloud-functions أيضًا.

{
  "name": "cloud-functions",
  "version": "1.0.0",
  "scripts": {
    "deploy": "firebase deploy --only functions"
  },
  "engines": {
    "node": "10"
  },
  "main": "dist/index.js",
  "dependencies": {
    "firebase-admin": "8.9.1",
    "firebase-functions": "3.3.0"
  },
  "devDependencies": {},
  "private": true
}

تم اختبار هذا والعمل. أنا أستخدم بناء جوجل كلاود. اطلب المزيد من المعلومات إذا لزم الأمر.

شكرا،

sowdri شكرا للمشاركة! هل تقوم بتشغيل webpack build from firebase.json functions.predeploy step أم أنك تقوم بتشغيله من النص البرمجي لإنشاء السحابة قبل استدعاء نشر firebase؟

@ 0x80 أقوم cloud build . لذلك وفقًا لـ firebase cli فهي مجرد مجموعة من الوظائف التي تم إنشاؤها باستخدام JS

sowdri شكرا على المثال. لقد انتهينا من استخدام نفس النهج في مشروع سابق لـ AWS Lambdas / Serverless: كان من الأسهل تحميل حزمة (Webpack) بدلاً من إجبار الأدوات على العمل مع monorepo الغزل.

لقد علقت أيضًا بهذا .... كل شيء سار بشكل جيد (حتى محاكي Firebase) حتى حاولت نشر الوظائف (بناء تطبيق الويب في رد الفعل ينشر بشكل جيد). :( نأمل أن يتمكن فريق Firebase من إضافة دعم لـ monorepos قريبًا.

أستخدم babel لحزم أخرى في monorepo الخاص بي ، لذلك أفضل البقاء مع ذلك. إذا لم يكن هناك شيء آخر ، فقد أجرب حزمة الويب ... والتي يبدو أنها تعمل مع البعض.

تحرير: لقد قمت بحل هذا باستخدام سجل حزمة GitHub. لذلك أقوم بنشر جميع حزمتي هناك ثم بالنسبة للخادم travis و functions ، يجب أن أقوم بإعداد السجل وتقديم رمز مميز للمصادقة (يتم عبر .npmrc ). ... يبدو كحل أنيق. خطرت لي فكرة هذا النهج هنا: https://medium.com/gdgeurope/how-to-use-firebase-cloud-functions-and-yarn-workspaces-24ca35e941eb

نعم ، تعرضت للعض من هذا أيضًا. كل شيء يعمل بشكل مثالي في firebase serve --only functions ولكن عند نشره ، لا يمكنه تحديد موقع الوحدة.

انتهى بي الأمر ببناء برنامج نصي صغير لإنشاء حزم لي. بينما يعكس هذا تفاصيل بيئتي ، مثل استخدام الوحدات وأسماء الحزم الخاصة بي التي تتطابق مع أسماء الدلائل الخاصة بي ، آمل أن يكون مفيدًا للآخرين.

استيراد fs من fs ؛
استيراد عملية_الطفل من "عملية_الأطفال" ؛

const innerPackagesFull = خريطة جديدة () ،

// ابحث عن جميع الأقسام للحزمة
const getDepsForPackage = (packageName) => {
const packageDir = packageName.split ("/") [1] ؛ // قد يحتاج هذا إلى التغيير من أجلك
حزمة constSpecFileName = ../${packageDir}/package.json ؛
const packageSpecFile = fs.readFileSync (packageSpecFileName) ،
const packageSpec = JSON.parse (packageSpecFile) ؛
const packageInternalDeps = Object.keys (
packageSpec.dependencies
) .filter ((key) => key.includes ("turing")) ؛ // سيحتاج هذا إلى التغيير من أجلك

حزمة constTgzName = ${packageName.replace("@", "").replace("/", "-")}-v${ packageSpec.version }.tgz ؛

InternalPackagesFull.set (packageName، {
packageSpecFileName ،
الحزمة
الحزمة
packageInternalDeps ،
packageTgzName ،
}) ؛

حزم constToProcess = packageInternalDeps.filter (
(internalDepName) =>! internalPackagesFull.has (internalDepName)
) ؛

packToProcess.forEach ((internalPackageName) =>
getDepsForPackage (internalPackageName)
) ؛
} ؛

const packageName = JSON.parse (fs.readFileSync ("./ package.json")). name ؛
child_process.execSync ( cp ./package.json ./package.json.org ) ؛
getDepsForPackage (اسم الحزمة) ،

// اكتب حزم محدثة - استخدم js الشائعة والمراجع هي ملفات tgz المحلية
[... internalPackagesFull.values ​​()]. ​​forEach ((internalDep) => {
const {packageSpec، packageSpecFileName، packageInternalDeps} = internalDep ؛

// تغيير نوع الحزمة
packageSpec.type = "commonjs" ؛ // قد يحتاج هذا إلى التغيير من أجلك

// حدد موقع القسم ليكون الملف المضغوط المضغوط
packageInternalDeps.forEach ((internalDepOfPackage) => {
const {packageTgzName} = internalPackagesFull.get (internalDepOfPackage) ،
packageSpec.dependencies [internalDepOfPackage] = ./${packageTgzName} ؛
}) ؛

fs.writeFileSync (
packageSpecFileName ،
JSON.stringify (packageSpec، null، "")
) ؛
}) ؛

// تشغيل بناء الغزل وحزمه
[... internalPackagesFull.values ​​()]. ​​forEach ((internalDep) => {
محاولة {
console.log ( Buliding ${internalDep.packageDir} ) ؛
child_process.execSync ("بناء الغزل"، {
cwd: ../${internalDep.packageDir} ،
}) ؛
console.log ( Packaging ${internalDep.packageDir} ) ؛
child_process.execSync ("حزمة الغزل"، {
cwd: ../${internalDep.packageDir} ،
}) ؛

if (packageName !== internalDep.packageSpec.name) {
  console.log(`Move to current directory ${internalDep.packageDir}`);
  child_process.execSync(
    `cp ../${internalDep.packageDir}/${internalDep.packageTgzName} .`,
    {
      cwd: ".",
    }
  );
}

} catch (e) {
console.log (ه) ؛
}
}) ؛

// العودة إلى هيكل الحزم القياسية
[... internalPackagesFull.values ​​()]
.filter ((internalDep) => اسم الحزمة! == internalDep.packageSpec.name)
.forEach ((internalDep) => {
مقدار ثابت {
الحزمة
packageSpecFileName ،
packageInternalDeps ،
} = internalDep ؛

// change the package type
packageSpec.type = "module"; // THIS MAY NEED TO CHANGE FOR YOU

// specify the location of the dep to be the packaged zip file
packageInternalDeps.forEach((internalDepOfPackage) => {
  packageSpec.dependencies[internalDepOfPackage] = "*";
});

fs.writeFileSync(
  packageSpecFileName,
  JSON.stringify(packageSpec, null, "  ")
);

}) ؛
""

لقد استخدمت الحل المقدم من sowdri (شكرًا على ذلك!) ، مع تعديل صغير بحيث يمكنني حذف وإعادة إنشاء الدليل dist/ بالكامل ، بما في ذلك package.json :

const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  ...,
  plugins: [
    new CopyPlugin({
      patterns: [{ from: 'package.dist.json', to: 'package.json' }],
    }),
  ],
};

ثم احتفظ بما يلي package.dist.json في جذر الحزمة:

{
  "name": "@package/name",
  "version": "0.0.1",
  "engines": {
    "node": "10"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "8.9.1",
    "firebase-functions": "3.3.0"
  },
  "private": true
}

قد يكون من الممكن إزالة التبعيات من هذا الملف والاعتماد على Webpack لتجميع تلك التبعيات أيضًا (مما يلغي الحاجة إلى إبقاء هذه التبعيات متزامنة مع package.json الرئيسي الخاص بك) ، لكني لم أحاول.

أستخدم مستودعًا واحدًا مع برنامج نصي لنسخ .firebaserc & firebase.json لجميع الأدلة ذات الصلة أو مشاريع Firebase باستخدام حزمة npm

أنشأت package.json من الحزمة الأصلية لنشر الوظائف.
image

هنا البرنامج النصي copyFiles.js :

const cpy = require('cpy');
const fs = require('fs');
const package = require('./package.json');
(async () => {
    await cpy(['./../package.json', './**/*.json', './**/.firebaserc'], '../out/', {
        parents: true,
        cwd: 'src'
    });
    const dirs = fs.readdirSync('./out/');
    const newPkg = {
        main: package.main,
        dependencies: package.dependencies,
        engines: package.engines,
    }
    dirs.forEach(dir => {
        fs.writeFileSync(`./out/${dir}/package.json`, JSON.stringify({ name: dir, ...newPkg }));
    })
    console.log('Files copied!', dirs);
})();

للنشر ، انتقل إلى ./out/[project-name]/firebase deploy --only=functions أو اكتب نصًا له أيضًا.

أواجه بعض تحذيرات Webpack مثل هذا:

تحذير في /Users/me/Development/myproject/node_modules/firebase-functions/lib/config.js 61: 23-42
التبعية الحرجة: طلب التبعية هو تعبير

هل وجدت طريقة لحلها ، أم أنك تتجاهلها / تقمعها؟

تمكنت من تشغيل الأشياء دون تحذيرات انتهى بي الأمر بالتهيئة أدناه. أدى استخدام أنماط regex بشكل خاص للأشكال الخارجية إلى إحداث فرق لأنه إذا كان لديك فقط "وظائف firebase" في العناصر الخارجية ، فلن تتم مطابقة أي استيراد تقوم به من وحدة فرعية وستظل المكتبة مضمنة في الحزمة الخاصة بك.

كنتيجة لمشكلات التجميع ، واجهت أيضًا بعض أخطاء النشر . لقد نسيت الاحتفاظ بها كمرجع.

const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  target: "node",
  mode: "production",
  entry: "./src/index.ts",
  devtool: "inline-source-map",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js", ".json"],
    alias: {
      "~": path.resolve(__dirname, "src"),
    },
  },
  output: {
    filename: "index.js",
    path: path.resolve(__dirname, "dist/bundled"),
    libraryTarget: "commonjs",
  },
  externals: ["express", /^firebase.+$/, /^@google.+$/],
  plugins: [
    new CopyPlugin({
      patterns: [{ from: "package.dist.json", to: "package.json" }],
    }),
  ],
};

اتضح أنه ليست هناك حاجة لحزمة منفصلة. الشيء المزعج لامتلاك هذا الملف هو بالطبع أنك تحتاج إلى تحديثه يدويًا في كل مرة تقوم فيها بتحديث أي من التبعيات المدرجة هناك. لذلك من السهل جدًا نسيان ذلك.

بدلاً من ذلك ، انقل جميع الحزم التي _لا تريد_ إدراجها في الحزمة الخاصة بك. dist.json إلى قائمة devDependencies ، واستخدم هذا الملف في المكون الإضافي لنسخة webpack. الآن لديك فقط package.json للتعامل معها 🎉

أيضًا ، لا أريد أن يكون إصدار nodejs المحلي الخاص بي بالضرورة هو نفس إصدار العقدة التي تم نشرها. اكتشفت أنه يمكنك الآن تحديد functions.runtime في ملف firebase.json. لذا قم بإخراج الحقل engines من package.json وبدلاً من ذلك قم بتعيين functions.runtime على "10" أو "12" في تهيئة Firebase.

هذا ما يبدو لي:

{
  "functions": {
    "source": "packages/cloud-functions/dist/bundled",
    "runtime": "12"
  },
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "emulators": {
    "functions": {
      "port": 5001
    },
    "firestore": {
      "port": 8080
    },
    "pubsub": {
      "port": 8085
    }
  }
}

كيف تتعامل مع خرائط المصدر؟ إذا قمت بتجميع وظائفي مع حزمة الويب باستخدام devtool: "inline-source-map" فلن يتم التقاطها في تقرير أخطاء Stackdriver. تضمين التغريدة

واجهت نفس الشيء في مشروعي وكان هذا مصدر قلق كبير لأنني لا أريد أن أقوم بنشر كل حزمة لمشروع جانبي. كان الأمر مزعجًا مواكبة عدة ملفات package.json أو الاضطرار إلى البناء خارج الحزمة. تبين أنه إذا قمت بتضمين الأقسام الخاصة بك على أنها اختيارية ، فستظل Lerna تلتقطها ولن يشتكي Firebase عند تحميلها.

سيوفر لك التكوين أدناه دعم dep مرتبطًا برمز مع package.json واحد!

package.json

{
  "name": "@your-package-name/functions",
  "version": "0.1.0",
  "scripts": {
    "build": "webpack"
  },
  "engines": {
    "node": "10"
  },
  "main": "dist/index.js",
  "dependencies": {
    "firebase-admin": "^8.10.0",
    "firebase-functions": "^3.6.1"
  },
  "optionalDependencies": {
    "@your-package-name/shared": "^0.1.0",
    "@your-package-name/utils": "^0.1.0"
  }
}

webpack.config.js

const path = require('path')

// The cost of being fancy I suppose
// https://github.com/firebase/firebase-tools/issues/653

module.exports = {
  target: 'node',
  mode: 'production',
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          configFile: 'tsconfig.build.json',
        },
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.json'],
  },
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, 'dist'),
    libraryTarget: 'commonjs',
  },
  externals: {
    'firebase-admin': 'firebase-admin',
    'firebase-functions': 'firebase-functions',
  },
}

firebase.json

{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "functions": {
    "source": "packages/functions"
  },
  "emulators": {
    "functions": {
      "port": 5476
    },
    "firestore": {
      "port": 4565
    },
    "ui": {
      "enabled": true
    }
  }
}

أنا أستخدم مساحات عمل الغزل ، لكني لست بحاجة أيضًا إلى ذكر أسماء الحزم المحلية الخاصة بي في ملفات package.json الأخرى. إنني أنشر كل من Firebase و Vercel بنجاح بدونها.

لست متأكدًا مما يجعلها تعمل. فقط لديك هذا التكوين القياسي في حزمة المستوى الأعلى الخاصة بي. json:

"workspaces": {
    "packages": [
      "packages/*"
    ]
  },

لا أمانع في الحصول على package.json في كل مجلد من مجلدات /pack / * ، لأن تشغيل yarn upgrade-interactive سيتعامل معها جميعًا دفعة واحدة. في بعض الحزم ، أجد أنه من المفيد أن أتمكن من إضافة برنامج نصي خصيصًا لهذا النطاق.

---- تعديل ----

نسيت أن أذكر أنني أستخدم Typescript مع مراجع المشروع. قد يكون لديه شيء ما يتعلق به. بالنسبة لـ Vercel ، أستخدم وحدات transpile التالية لتضمين الكود المشترك الخاص بي في الحزمة.

أنا أستخدم إعدادًا مشابهًا لـ @ 0x80 و sowdri لإنجاح هذا الأمر ، ولكن بدلاً من نقل التبعيات المحلية الخاصة بي إلى devDependencies (التي لم تنجح في الواقع ، أعتقد أنني كنت أفتقد خطوة) واستخدام copy-webpack-plugin ، أنا أستخدم generate-package-json-webpack-plugin لإنشاء الحزمة الخاصة بي. json في مجلد dist. يؤدي هذا إلى إنشاء قائمة التبعيات الخاصة بي مما تتطلبه التعليمات البرمجية الناتجة بالفعل ، لذلك إذا كانت مجمعة أو تبعية dev ، فلن يتم تضمينها في الحزمة الناتجة. json.

لقد قمت أيضًا بإعداد العناصر الخارجية الخاصة بي باستخدام webpack-node-externals لجعل كل شيء خارجيًا باستثناء حزم monorepo المرتبطة التي أستخدمها لمطابقة بادئة اسم المشروع. لقد أضفت أيضًا تعبيرات regex لحزم Firebase @ 0x80 التي تم نشرها على أنها عناصر خارجية إضافية.

هذا هو التكوين الخاص بي

/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const GeneratePackageJsonPlugin = require("generate-package-json-webpack-plugin");

const basePackage = {
  name: "@project/functions",
  version: "1.0.0",
  main: "./index.js",
  scripts: {
    start: "yarn run shell"
  },
  engines: {
    node: "12"
  }
};

module.exports = {
  target: "node",
  mode: "production",
  entry: "./src/index.ts",
  devtool: "inline-source-map",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js", ".json"],
    alias: {
      "@": path.resolve(__dirname, "src"),
      "@root": path.resolve(__dirname, "./"),
      "@types": path.resolve(__dirname, "src/@types"),
      "@utils": path.resolve(__dirname, "src/utils")
    }
  },
  output: {
    filename: "index.js",
    path: path.resolve(__dirname, "dist"),
    libraryTarget: "commonjs"
  },
  externals: [
    /^firebase.+$/,
    /^@google.+$/,
    nodeExternals({
      allowlist: [/^@project/]
    })
  ],
  plugins: [new GeneratePackageJsonPlugin(basePackage)]
};

الميزة الأخرى لاستخدام حزمة الويب لتجميع الكود هي أنه يمكنني أخيرًا استخدام الأسماء المستعارة للوحدة :)

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات