Firebase-tools: Unterstützung von Mono-Repos in der Bereitstellung

Erstellt am 31. Jan. 2018  ·  47Kommentare  ·  Quelle: firebase/firebase-tools

Siehe: https://github.com/firebase/firebase-functions/issues/172

Versions Information

3.17.3

Schritte zum Reproduzieren

Erwartetes Verhalten

Tatsächliches Verhalten

feature request

Hilfreichster Kommentar

+1 dazu, Cloud-Funktionen müssen normalerweise gemeinsamen Code (z. B. Schnittstellen) mit anderen Apps teilen und eine gute Möglichkeit, damit umzugehen, ist ein Monorepo (z. B. lerna) oder die direkte Verwendung von Symlinks. Letzteres habe ich genommen und gelöst, indem ich einige Skripte erstellt habe. Das Konzept ist ganz einfach: Ich kopiere alles was benötigt wird in das Funktionsverzeichnis und entferne es danach

So habe ich es mit dieser Verzeichnisstruktur gemacht:
```

  • Wurzel/
    | - .firebaserc
    | - firebase.json
    | - ...
  • Funktionen/
    | - src/
    | - Paket.json
    | - pre-deploy.js
    | - post-deploy.js
    | - ....
  • geteilt/
    | - src/
    | - Paket.json
    | - ....
content of `pre-deploy.js`

const fs = require("fs-extra");

const packageJsonPath = "./package.json";
const packageJson = require(packageJsonPath);

(asynchron () => {
warten fs.remove( ./shared );
warten 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 = require(packageJsonPath);
const fs = require("fs-extra");

(asynchron () => {
warten 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)

"funktionen": {
"Quelle": "Funktionen",
"vorbereiten": [
"npm --prefix "$RESOURCE_DIR" vor der Bereitstellung ausführen"
],
"nach der Bereitstellung": [
"npm --prefix "$RESOURCE_DIR" nach der Bereitstellung ausführen"
]
},
```

Wenn Sie den Build durchführen, sollten Sie im Verzeichnis dist oder lib jetzt zwei Geschwister haben: Funktionen und Shared (dies geschah aufgrund der gemeinsamen Abhängigkeit). Stellen Sie sicher, dass die Funktionen package.json main aktualisiert werden, dass sie auf lib/functions/src/index.js , damit die Bereitstellung funktioniert.

Im Moment ist es gelöst, aber das ist ein Workaround, keine Lösung. Ich denke, dass Firebase-Tools Symlinks wirklich unterstützen sollten

Alle 47 Kommentare

Dies scheint in meinem Setup zu funktionieren, dh deploy holt Pakete vom Stamm node_modules , obwohl package.json dafür unter dem api/ Arbeitsbereich ( Ich habe einen anderen Ordnernamen anstelle von functions/ ). Gibt es hier noch etwas zu reparieren?

BEARBEITEN: Außerdem kopiere ich package.json in api/dist , um von deploy .

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

2 Verschachtelungsebenen lösen also immer noch die Wurzel node_modules erfolgreich auf.

@dinvlad könnten Sie ein Repo teilen?

@orouz leider noch nicht, es ist

Hat es jemand geschafft, dieses Problem zu lösen? Das Teilen eines einfachen Beispielprojekts wäre sehr nützlich.

@audkar Derzeit verwende ich nur lerna.js.org und es ist der Befehl run , um ein npm-Skript in jedem Unterordner mit dieser Ordnerstruktur auszuführen:

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

Es bleibt dem Benutzer überlassen, sicherzustellen, dass die firebase.json Dateien für jeden Dienst nicht aufeinander treten. Einfache Konventionen bei der Verwendung von Funktionsgruppen und dem Targeting auf mehrere Sites bedeutet, dass dies für Cloud Functions und Hosting gelöst ist. Ich habe noch keine Lösung für Firestore/GCS-Regeln, obwohl eine Aufteilung möglicherweise nicht ideal ist ...

bereits hier besprochen - https://github.com/firebase/firebase-tools/issues/1116

@jthegedus danke für deine Antwort. Aber ich denke, die Ausgabe dieses Tickets ist anders. Ich versuche, Garn-Arbeitsbereiche zu verwenden. Und es scheint, dass Firebase-Tools beim Hochladen von Funktionen keine Symlink-Abhängigkeiten aufnehmen

Ah fair genug, ich bin selbst diesem Kaninchenbau aus dem Weg gegangen

Könnten Sie näher erläutern, was das Problem ist? Wie oben erwähnt, verwende ich nur nacktes Garn mit den Arbeitsbereichen api und app , und ich erstelle sie mit yarn workspace api build && yarn workspace app build (mit build Skript für jeden Arbeitsplatz). Die Build-Skripte
1) Kompiliere TS-Code mit outDir in api/dist bzw. app/dist
2) Kopieren Sie die entsprechenden package.json Dateien in dist Verzeichnisse
3) kopiere yarn.lock aus dem _root_ Ordner in dist Verzeichnisse

Dann führe ich einfach yarn firebase deploy aus dem _root_-Ordner aus und es nimmt sowohl api/dist als auch app/dist ohne Probleme auf. Mein firebase.json sieht aus wie

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

Leider kann ich immer noch nicht den vollständigen Code teilen, aber dieses Setup ist alles, was zählt, afaik.

Außerdem könnte ich mich irren, aber ich denke, das firebase deploy Skript verwendet nicht Ihr node_modules Verzeichnis. Ich denke, es nimmt nur den Code package.json und yarn.lock aus den dist Verzeichnissen auf und erledigt den Rest.

Das stimmt. Der Standardwert von "functions.ignore" in firebase.json ist
["node_modules"], also wird es nicht hochgeladen. Ich glaube, das kannst du überschreiben
wenn Sie jedoch einige lokale Module versenden möchten.

Am Mo, 17.06.2019, 18:58 Uhr Denis Loginov [email protected]
schrieb:

Außerdem könnte ich mich irren, aber ich denke, das Firebase-Bereitstellungsskript tut dies nicht
Verwenden Sie tatsächlich Ihr node_modules-Verzeichnis. Ich denke, es nimmt einfach die auf
cod, package.json und Garn.lock aus den Verzeichnissen dist und führt dies aus
sich ausruhen.


Sie erhalten dies, weil Sie diesen Thread abonniert haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/firebase/firebase-tools/issues/653?email_source=notifications&email_token=ACATB2U73VS2KIILUVRFFB3P3A6NPA5CNFSM4EOR24GKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXH46KTDNQP
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/ACATB2U3Q2TLVBICRJ3B5OLP3A6NPANCNFSM4EOR24GA
.

@dinvlad Ja, es erfordert package.json und die von Ihnen verwendete Sperrdatei, da Deps in der Cloud nach der Bereitstellung installiert werden.

Ich glaube, das ursprünglich in der anderen Ausgabe beschriebene Szenario war die Verwendung eines freigegebenen Pakets innerhalb des Arbeitsbereichs und einige Probleme beim Hochziehen des Bereichs. Da ich kein Garn auf diese Weise verwendet habe, kann ich nur spekulieren, was ich dort gelesen habe.

@samtstern @jthegedus danke, gut zu wissen!

Es scheint, dass wir alle über unterschiedliche Probleme sprechen. Ich werde versuchen, yarn workspaces Problem zu beschreiben.

Problematisches Projekt

Projektlayout

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

_./paket.json_

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

_Funktionen/Paket.json_

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

Problem

Fehler bei der Funktionsbereitstellung:

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

Die Funktionen funktionieren lokal auf dem Emulator einwandfrei.

Lösungen versucht

Hochladen von node_modules (mit functions.ignore in _firebase.json_). Ergebnis ist gleich.

Ich vermute, dass es daran liegt, dass utilities als Syslink in _node-modules_ erstellt wird node_modules/utilities -> ../../utilities

Könnte es sein, dass Firebase-Tools beim Hochladen keine Inhalte von symbolverknüpften Modulen einbezieht (keine Dereferenzierung)?

Entschuldigung, könnten Sie klären, in welchem ​​Ordner Ihr firebase.json lebt (und seinen Konfigurationsabschnitt für functions )?

_firebase.json_ befand sich im Stammordner. Die Konfiguration war Standard. etw so:

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

alles wurde wie erwartet bereitgestellt (einschließlich _node_modules_), außer node_modules/utilities das ein Symlink ist.


Ich kann dieses Problem umgehen, indem ich einige Skripte schreibe, die:

  • Pakete für jeden Arbeitsbereich erstellen ( yarn pack ). zB erzeugt dies _utilities.tgz_.
  • Verschieben der gesamten Ausgabe in ein bestimmtes Verzeichnis.
  • Ändern von _package.json_, um tgz-Dateien für Arbeitsbereichsabhängigkeiten zu verwenden. zB dependencies { "utilities": "1.0.0" -> dependencies { "utilities": "file:./utilities.tgz"
  • Bereitstellen dieses Verzeichnisses in Firebase

Inhalt des Ausgabeverzeichnisses vor dem Hochladen:

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

@audkar Heute bin ich auf das gleiche Problem

Ich bin neu in den Arbeitsbereichen Lerna und Yarn. So wie ich es verstanden habe, können Sie auch einfach Lerna verwenden. Würde das irgendwie helfen?

Dein Workaround erscheint mir etwas kompliziert 🤔

Sie fragen sich auch, wofür `--cwd "$RESOURCE_DIR" ist?

--cwd steht für "aktuelles Arbeitsverzeichnis" und $RESOURCE_DIR enthält den Wert für das Quellverzeichnis (in diesem Fall functions ). Wenn Sie dieses Flag hinzufügen, wird yarn in functions dir statt in root ausgeführt

@audkar Ah ich yarn workspace functions lint und yarn workspace functions build

@dinvlad Mir ist unklar, warum Sie auf den Ordner dist abzielen und Dinge dorthin kopieren. Wenn Sie für dist erstellen, aber die package.json dort lassen, wo sie ist, und main auf dist/index.js sollten die Dinge genauso funktionieren, nein? Sie sollten dann source auf api anstelle von api/dist setzen.

@dinvlad Ich habe den Befehl yarn workspace aus Ihren Kommentaren gelernt, kann ihn aber aus irgendeinem Grund nicht zum Laufen bringen . Siehe hier . Irgendeine Idee?

Sorry, dass ich hier etwas vom Thema abweiche. Vielleicht in SO kommentieren, um das Rauschen zu minimieren.

@0x80 Ich kopiere package.json nach api/dist und zeige firebase.json auf api/dist damit nur die "gebauten" Dateien in die Cloud-Funktion gepackt werden. Ich bin mir nicht sicher, was passiert, wenn ich firebase.json auf api zeige - vielleicht ist es immer noch schlau genug, nur das zu verpacken, was sich in api/dist (basierend auf main Attribut in package.json ). Aber ich dachte, es wäre sauberer, einfach auf api/dist .

Zu yarn workspace , ich habe auf SO geantwortet ;)

@dinvlad es bündelt die Wurzel dessen, worauf Sie es verweisen, aber Sie können alles, was Sie nicht möchten, in die Ignorierliste von firebase.json aufnehmen.

Ich habe jetzt einen ähnlichen Workaround wie @audkar verwendet.

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

Dann lautet das pre-deploy-cloud-functions-Skript:

#!/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

Und Pakete/Cloud-Funktionen hat eine zusätzliche gitignore-Datei:

yarn.lock
*.tgz

das hat bei mir funktioniert

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

und im root/firebase.json :
```
{
"funktionen": {
"predeploy": "npm --prefix "$RESOURCE_DIR" Build ausführen",
"source": "Pakete/Funktionen"
}
}
````

@kaminskypavel hängen Ihre Pakete/Funktionen von Paketen/Paket1 (oder einem anderen Geschwisterpaket) ab?

@0x80 positiv.

Ich glaube, ich habe etwas grundlegendes an Monorepos falsch verstanden. Ich ging davon aus, dass Sie ein Paket freigeben und eine App mithilfe dieses Pakets bereitstellen können, ohne das freigegebene Paket tatsächlich in NPM zu veröffentlichen.

Dies scheint nicht möglich zu sein, da Bereitstellungen wie Firebase oder Now.sh normalerweise den Code hochladen und dann in der Cloud eine Installation und einen Build durchführen. Hab ich recht?

@kaminskypavel Ich habe Ihren Ansatz ausprobiert und er funktioniert, aber erst nachdem ich mein Paket zuerst in NPM veröffentlicht habe. Da das Paket in meinem Fall privat ist, habe ich zunächst die Fehlermeldung "nicht gefunden" erhalten, daher musste ich auch meine .npmrc-Datei wie hier beschrieben zum Stammverzeichnis des Cloud-Funktionspakets hinzufügen

@audkar Veröffentlichen Sie Ihr allgemeines Paket in NPM oder versuchen Sie wie ich, mit freigegebenem Code

@ 0x80 Ich stimme Ihnen bei diesem Verständnis zu - ich denke, Firebase Function-Bereitstellungen gehen nur (fälschlicherweise) davon aus, dass alle in package.json genannten Pakete auf npm verfügbar sind, um Bereitstellungen zu beschleunigen.

Da Garn-Workspace-Setups immer beliebter werden, werden sich vermutlich immer mehr Leute wundern, dass sie keine symbolverknüpften Pakete in Firebase Functions verwenden können – zumal sie bis zur Bereitstellung einwandfrei funktionieren.

Mit npm, das die Unterstützung für Arbeitsbereiche hinzufügt , haben wir einen Ökosystemstandard dafür, wie lokale Pakete funktionieren sollten.

Da dieses Problem über ein Jahr alt ist, gibt es hier ein Update von Firebase zu Plänen (oder fehlenden Plänen)?

Ich denke, es ist eine ziemlich coole Gelegenheit – die Palette der Dienste von Firebase verlangt nach einem guten Monorepo-Setup.

+1 dazu, Cloud-Funktionen müssen normalerweise gemeinsamen Code (z. B. Schnittstellen) mit anderen Apps teilen und eine gute Möglichkeit, damit umzugehen, ist ein Monorepo (z. B. lerna) oder die direkte Verwendung von Symlinks. Letzteres habe ich genommen und gelöst, indem ich einige Skripte erstellt habe. Das Konzept ist ganz einfach: Ich kopiere alles was benötigt wird in das Funktionsverzeichnis und entferne es danach

So habe ich es mit dieser Verzeichnisstruktur gemacht:
```

  • Wurzel/
    | - .firebaserc
    | - firebase.json
    | - ...
  • Funktionen/
    | - src/
    | - Paket.json
    | - pre-deploy.js
    | - post-deploy.js
    | - ....
  • geteilt/
    | - src/
    | - Paket.json
    | - ....
content of `pre-deploy.js`

const fs = require("fs-extra");

const packageJsonPath = "./package.json";
const packageJson = require(packageJsonPath);

(asynchron () => {
warten fs.remove( ./shared );
warten 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 = require(packageJsonPath);
const fs = require("fs-extra");

(asynchron () => {
warten 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)

"funktionen": {
"Quelle": "Funktionen",
"vorbereiten": [
"npm --prefix "$RESOURCE_DIR" vor der Bereitstellung ausführen"
],
"nach der Bereitstellung": [
"npm --prefix "$RESOURCE_DIR" nach der Bereitstellung ausführen"
]
},
```

Wenn Sie den Build durchführen, sollten Sie im Verzeichnis dist oder lib jetzt zwei Geschwister haben: Funktionen und Shared (dies geschah aufgrund der gemeinsamen Abhängigkeit). Stellen Sie sicher, dass die Funktionen package.json main aktualisiert werden, dass sie auf lib/functions/src/index.js , damit die Bereitstellung funktioniert.

Im Moment ist es gelöst, aber das ist ein Workaround, keine Lösung. Ich denke, dass Firebase-Tools Symlinks wirklich unterstützen sollten

@michelepatrassi inspiriert von dem, was Sie mich daran erinnert haben, habe ich eine firelink Bibliothek erstellt, um diesen Fall zu verwalten. Es verwendet intern rsync um rekursive Dateien zu kopieren.

https://github.com/rxdi/firelink

npm i -g @rxdi/firelink

Grundlegende Verwendung
Angenommen, Sie haben einen Monorepo-Ansatz und Ihre Pakete befinden sich 2 Ebenen unterhalb des aktuellen Verzeichnisses, in dem sich package.json befindet.

package.json

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

Wenn firelink , werden Pakete kopiert, die sich auf Ordner beziehen, dann werden vorhandene Pakete mit der lokalen Modulinstallation "@graphql/database": "file:./.packages/database", dann wird der Befehl firebase und der Rest der Argumente von firelink Befehl.
Grundsätzlich ist firelink ein Ersatz für firebase CLI, da es firebase am Ende hervorbringt, wenn er seinen Job beendet hat, indem er packages kopiert und package.json modifiziert!

Grüße!

Wir sind gerade davon gebissen worden, ich denke, Monorepos wird zum Standard und dies sollte standardmäßig unterstützt werden.

Leute, eine einfache Lösung für dieses Problem besteht darin, Webpack zu verwenden, um Ihre Cloud-Funktionen zu bündeln und aus dem gebündelten Verzeichnis bereitzustellen. Hier ist eine grundlegende Webpack-Datei, die ich verwende. Während des Builds wird der gesamte Funktionscode, einschließlich der im Mono-Repository aufgelösten Abhängigkeiten, in einen Ordner der obersten Ebene gepackt (in diesem Fall ist es webpack/cloud-functions , es könnte alles sein, was Sie konfigurieren).

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'
  }
};

Und schließlich finden Sie in Ihrer firebase.json Datei diesen Ordner für die Bereitstellung.

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

Denken Sie daran, auch eine package.json Datei im webpack/cloud-functions Ordner zu haben.

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

Das ist getestet und funktioniert. Ich verwende Google Cloud-Build. Fordern Sie bei Bedarf weitere Informationen an.

Vielen Dank,

@sowdri Danke fürs Teilen! Lösen Sie den Webpack-Build über den Schritt firebase.json functions.predeploy oder über das Cloud-Build-Skript aus, bevor Sie firebase deploy aufrufen?

@0x80 Ich baue es im Schritt cloud build . Laut firebase cli es sich also nur um eine Reihe von Funktionen, die mit JS erstellt wurden

@sowdri Danke für das Beispiel. Wir haben in einem früheren Projekt für AWS Lambdas / Serverless denselben Ansatz verwendet: Es war einfacher, ein (Webpack-)Bundle hochzuladen, als die Tools dazu zu zwingen, mit dem Garn-Monorepo zu arbeiten.

Ich stecke auch damit fest .... alles funktionierte gut (sogar Firebase-Emulator), bis ich versuchte, Funktionen bereitzustellen (Web-App-Build in React stellt OK bereit). :( Hoffentlich kann das Firebase-Team bald Unterstützung für Monorepos hinzufügen.

Ich verwende Babel für andere Pakete in meinem Monorepo, also würde ich es vorziehen, dabei zu bleiben. Wenn nichts anderes, kann ich das Webpack ausprobieren ... was für einige zu funktionieren scheint.

EDIT: Ich löste dies durch GitHub Paket Registrierung mit. Also veröffentliche ich alle meine Pakete dort und dann muss ich für travis und functions die Registry einrichten und ein Token für die Authentifizierung bereitstellen (über .npmrc ). ...scheint eine elegante Lösung zu sein. Die Idee zu diesem Ansatz habe ich hier: https://medium.com/gdgeurope/how-to-use-firebase-cloud-functions-and-yarn-workspaces-24ca35e941eb

Ja, wurde auch davon gebissen. Alles funktionierte perfekt in firebase serve --only functions aber bei der Bereitstellung konnte kein Modul gefunden werden.

Am Ende habe ich ein kleines Skript erstellt, um Pakete für mich zu erstellen. Obwohl dies Einzelheiten meiner Umgebung widerspiegelt, wie die Verwendung von Modulen und meiner Paketnamen, die mit meinen Verzeichnisnamen übereinstimmen, hoffe ich, dass es für andere nützlich ist.

```fs aus "fs" importieren;
importiere child_process aus "child_process";

const internalPackagesFull = new Map();

// Finde alle Deps für das Paket
const getDepsForPackage = (Paketname) => {
const packageDir = packageName.split("/")[1]; // DAS MUSS SICH FÜR SIE ÄNDERN
const packageSpecFileName = ../${packageDir}/package.json ;
const packageSpecFile = fs.readFileSync(packageSpecFileName);
const packageSpec = JSON.parse(packageSpecFile);
const packageInternalDeps = Object.keys(
packageSpec.Abhängigkeiten
).filter((Schlüssel) => key.includes("turing")); // DAS MUSS SICH FÜR DICH ÄNDERN

const PaketTgzName = ${packageName.replace("@", "").replace("/", "-")}-v${ packageSpec.version }.tgz ;

internalPackagesFull.set(packageName, {
PaketSpecFileName,
PaketSpec,
PaketDir,
PaketInternalDeps,
PaketTgzName,
});

const packageToProcess = packageInternalDeps.filter(
(internalDepName) => !internalPackagesFull.has(internalDepName)
);

PaketeToProcess.forEach((internalPackageName) =>
getDepsForPackage(internalPackageName)
);
};

const packageName = JSON.parse(fs.readFileSync("./package.json")).name;
child_process.execSync( cp ./package.json ./package.json.org );
getDepsForPackage(Paketname);

// schreibe aktualisierte Pakete - verwende allgemeine js und Referenzen sind lokale tgz-Dateien
[...internalPackagesFull.values()].forEach((internalDep) => {
const { packageSpec, packageSpecFileName, packageInternalDeps } = internalDep;

// den Pakettyp ändern
packageSpec.type = "commonjs"; // DAS MUSS SICH FÜR SIE ÄNDERN

// Geben Sie den Speicherort des dep als gepackte ZIP-Datei an
packageInternalDeps.forEach((internalDepOfPackage) => {
const { packageTgzName } = internalPackagesFull.get(internalDepOfPackage);
packageSpec.dependencies[internalDepOfPackage] = ./${packageTgzName} ;
});

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

// Garn aufbauen und packen
[...internalPackagesFull.values()].forEach((internalDep) => {
Versuchen {
console.log( Buliding ${internalDep.packageDir} );
child_process.execSync("Garnbuild", {
cwd: ../${internalDep.packageDir} ,
});
console.log( Packaging ${internalDep.packageDir} );
child_process.execSync("Garnpaket", {
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: ".",
    }
  );
}

} fangen (e) {
Konsole.log(e);
}
});

// gehe zurück zur Standardpaketstruktur
[...internalPackagesFull.values()]
.filter((internalDep) => packageName !== internalDep.packageSpec.name)
.forEach((internalDep) => {
Konstante {
PaketSpec,
PaketSpecFileName,
PaketInternalDeps,
} = 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, "  ")
);

});
```

Ich habe die von @sowdri bereitgestellte Lösung verwendet (danke dafür!), mit einer kleinen Optimierung, damit ich das gesamte dist/ Verzeichnis, einschließlich des sekundären package.json frei löschen und neu generieren kann:

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

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

Und dann behalte ich die folgenden package.dist.json im Paket-Root:

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

Es ist möglicherweise möglich, die Abhängigkeiten aus dieser Datei zu entfernen und sich auf Webpack zu verlassen, um diese ebenfalls zu bündeln (wodurch die Notwendigkeit beseitigt wird, diese Abhängigkeiten mit Ihrem Haupt package.json synchron zu halten), aber ich habe es nicht versucht.

Ich verwende ein einzelnes Repository mit einem Skript, um .firebaserc & firebase.json für alle relevanten Verzeichnisse oder Firebase-Projekte mit dem npm cpy-Paket zu kopieren, wobei die dir-Struktur in das Ausgabeverzeichnis

Ich erstelle package.json aus der ursprünglichen package.json für die Funktionsbereitstellung.
image

Hier ist das copyFiles.js Skript:

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);
})();

für die Bereitstellung gehen Sie zu ./out/[project-name]/firebase deploy --only=functions oder schreiben Sie auch ein Skript dafür.

Ich stoße auf einige Webpack-Warnungen wie diese:

WARNUNG in /Users/me/Development/myproject/node_modules/firebase-functions/lib/config.js 61:23-42
Kritische Abhängigkeit: Die Anforderung einer Abhängigkeit ist ein Ausdruck

Haben Sie einen Weg gefunden, diese zu lösen, oder ignorieren/unterdrücken Sie sie?

Ich habe es geschafft, die Dinge ohne Warnungen zum Laufen zu bringen 🥳 Am Ende habe ich die Konfiguration unten. Besonders die Verwendung der Regex-Muster für die Externals machte einen Unterschied, denn wenn Sie nur "firebase-functions" in Ihren Externals haben, wird jeder Import, den Sie aus einem Submodul vornehmen, nicht abgeglichen und die Bibliothek ist immer noch in Ihrem Bundle enthalten.

Aufgrund von Bündelungsproblemen sind bei der Bereitstellung auch einige kryptische @grpc- Fehler

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" }],
    }),
  ],
};

Es stellt sich heraus, dass kein separates package.dist.json erforderlich ist. Das Ärgerliche an dieser Datei ist natürlich, dass Sie sie jedes Mal manuell aktualisieren müssen, wenn Sie eine der dort aufgeführten Abhängigkeiten aktualisieren. Das vergisst man also sehr leicht.

Verschieben Sie stattdessen alle Pakete, die Sie _nicht_ in Ihrer package.dist.json auflisten würden, in die Liste devDependencies und verwenden Sie diese Datei einfach im Webpack-Kopier-Plugin. Jetzt haben Sie nur noch ein einziges package.json, das Sie bearbeiten müssen 🎉

Außerdem möchte ich nicht, dass meine lokale nodejs-Version unbedingt mit der von den Funktionen bereitgestellten Knotenversion übereinstimmt. Ich habe festgestellt, dass Sie jetzt functions.runtime in der Datei firebase.json angeben können. Nehmen Sie also das Feld engines aus der package.json heraus und setzen Sie stattdessen functions.runtime in Ihrer Firebase-Konfiguration auf "10" oder "12".

So sieht es bei mir aus:

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

Wie gehen Sie mit Source Maps um? Wenn ich meine Funktionen mit Webpack mit devtool: "inline-source-map" bündele, wird es nicht in der Stackdriver-Fehlerberichterstattung aufgenommen. @sowdri

Ich bin bei meinem Projekt auf dasselbe gestoßen und das war eine ziemlich große Enttäuschung, da ich nicht jedes Paket für ein Nebenprojekt veröffentlichen möchte. Es war ärgerlich, mit mehreren package.json Dateien Schritt zu halten oder außerhalb des Pakets bauen zu müssen. Es stellt sich heraus, dass Lerna, wenn Sie Ihre Deps als optional angeben, sie immer noch aufnimmt und Firebase sich beim Hochladen nicht beschwert.

Mit der folgenden Konfiguration erhalten Sie mit einem einzigen package.json symlinked dep-Unterstützung!

Paket.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
    }
  }
}

Ich verwende Garn-Arbeitsbereiche, muss aber auch meine lokalen Paketnamen in anderen package.json-Dateien nicht erwähnen. Ich stelle die Bereitstellung sowohl für Firebase als auch für Vercel erfolgreich ohne sie bereit.

Ich bin mir nicht sicher, was es funktioniert. Habe einfach diese Standardkonfiguration in meinem Top-Level-Paket.json:

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

Es macht mir nichts aus, eine package.json in jedem der /packages/*-Ordner zu haben, da das Ausführen von yarn upgrade-interactive sie alle auf einmal verarbeiten wird. In einigen Paketen finde ich es nützlich, Skripte speziell für diesen Bereich hinzufügen zu können.

---- bearbeiten ----

Ich habe vergessen zu erwähnen, dass ich Typescript mit Projektreferenzen verwende. Das könnte damit zu tun haben. Für Vercel verwende ich next-transpile-modules, um meinen freigegebenen Code in das Bundle aufzunehmen.

Ich verwende ein ähnliches Setup wie @0x80 und @sowdri , damit dies funktioniert, aber anstatt meine lokalen Abhängigkeiten in devDependencies zu verschieben (was bei mir eigentlich nicht funktioniert hat, denke ich, dass ich einen Schritt verpasst habe) und verwende copy-webpack-plugin , ich verwende generate-package-json-webpack-plugin , um meine package.json im Ordner dist zu erstellen. Dadurch wird meine Abhängigkeitsliste aus dem erstellt, was der resultierende Code tatsächlich erfordert. Wenn er also gebündelt oder eine Dev-Abhängigkeit ist, ist er nicht in der resultierenden package.json enthalten.

Ich habe auch meine Externals mit webpack-node-externals , um alles extern zu machen, mit Ausnahme meiner symbolverknüpften Monorepo-Pakete, die ich mit Regex verwende, um dem Projektnamenspräfix zu entsprechen. Ich habe auch die Regex-Ausdrücke für die Firebase-Pakete @0x80 hinzugefügt, die als zusätzliche Externals gepostet wurden.

Das ist meine Konfiguration

/* 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)]
};

Der andere Vorteil bei der Verwendung von Webpack zum Bündeln des Codes ist, dass ich endlich Modulaliase verwenden kann :)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen