Ver: https://github.com/firebase/firebase-functions/issues/172
3.17.3
Esto parece funcionar en mi configuración, es decir, deploy
recoge paquetes de la raíz node_modules
, aunque package.json
para eso se encuentra debajo del espacio api/
trabajo functions/
). ¿Hay algo más que deba arreglarse aquí?
EDITAR: Además, copio package.json
en api/dist
para ser utilizado por deploy
.
// firebase.json
...
"functions": {
"source": "api/dist"
},
...
Por lo tanto, 2 niveles de anidamiento aún resuelven la raíz node_modules
éxito.
@dinvlad, ¿ podrías compartir un repositorio?
@orouz lamentablemente todavía no, es de código cerrado por ahora.
¿Alguien logró abordar este problema? Compartir un proyecto de ejemplo simple sería muy útil.
@audkar Actualmente solo uso lerna.js.org y es el comando run
para ejecutar un script npm en cada subcarpeta con esta estructura de carpeta:
- service1/
| - .firebaserc
| - firebase.json
- service2/
| - .firebaserc
| - firebase.json
- app1/
| - .firebaserc
| - firebase.json
- app2/
| - .firebaserc
| - firebase.json
- firestore/
| - firestore.rules
| - firestore.indexes.json
- etc...
Asegurar que los archivos firebase.json
para cada servicio no se pisoteen entre sí queda en manos del usuario. Las convenciones simples de usar grupos de funciones y la segmentación por nombres de sitios múltiples significan que esto se resuelve para Cloud Functions y Hosting. Todavía no tengo una solución para las reglas de Firestore / GCS, aunque dividirlas puede no ser lo ideal ...
discutido aquí anteriormente - https://github.com/firebase/firebase-tools/issues/1116
@jthegedus gracias por su respuesta. Pero creo que el tema de este boleto es diferente. Estoy tratando de usar espacios de trabajo de hilo. Y parece que las herramientas de base de fuego no recogen las dependencias de enlaces simbólicos al cargar funciones
Ah, bastante bien, yo mismo he evitado esa madriguera
¿Podría explicarnos cuál es el problema? Como se mencionó anteriormente, solo uso hilo desnudo con api
y app
espacios de trabajo en él, y los construyo usando yarn workspace api build && yarn workspace app build
(con build
script específico para cada uno espacio de trabajo). Los scripts de construcción
1) compile el código TS con outDir
en api/dist
y app/dist
respectivamente
2) copiar los correspondientes package.json
archivos en dist
directorios
3) copie yarn.lock
de la carpeta _root_, en los directorios dist
Luego ejecuto yarn firebase deploy
desde la carpeta _root_, y recoge tanto api/dist
como app/dist
sin ningún problema. Mi firebase.json
parece
"functions": {
"source": "api/dist"
},
"hosting": {
"public": "app/dist",
Desafortunadamente, todavía no puedo compartir el código completo, pero esta configuración es todo lo que importa, afaik.
Además, podría estar equivocado, pero creo que el script firebase deploy
realidad no usa su directorio node_modules
. Creo que solo toma el código, package.json
y yarn.lock
de los directorios dist
, y hace el resto.
Eso es cierto. El valor predeterminado de "functions.ignore" en firebase.json es
["node_modules"] por lo que no se carga. Creo que puedes anular eso
aunque si desea enviar algunos módulos locales.
El lunes, 17 de junio de 2019, 6:58 p.m. Denis Loginov [email protected]
escribió:
Además, podría estar equivocado, pero creo que el script de implementación de la base de fuego no lo hace.
realmente use su directorio node_modules. Creo que simplemente recoge el
cod, package.json y yarn.lock de los directorios dist, y hace el
descansar.-
Estás recibiendo esto porque estás suscrito a este hilo.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/firebase/firebase-tools/issues/653?email_source=notifications&email_token=ACATB2U73VS2KIILUVRFFB3P3A6NPA5CNFSM4EOR24GKYY3PNVWWK3TUL52HS4DFVREXP63JSMVBW5 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ACATB2U3Q2TLVBICRJ3B5OLP3A6NPANCNFSM4EOR24GA
.
@dinvlad Sí, requiere el package.json
y el archivo de bloqueo que use para instalar los departamentos en la implementación de la publicación en la nube.
Creo que el escenario que se describió originalmente en el otro número fue el uso de un paquete compartido dentro del espacio de trabajo y algunos problemas con la elevación del alcance. Como no estaba usando hilo de esta manera, solo puedo especular a partir de lo que he leído allí.
@samtstern @jthegedus gracias, ¡bueno saberlo!
Parece que todos hablamos de diferentes problemas. Intentaré describir el problema yarn workspaces
.
diseño del proyecto
- utilities/
| - package.json
- functions/
| - package.json
- package.json
_./package.json_
{
"private": true,
"workspaces": ["functions", "utilities"]
}
_functions / package.json_
{
<...>
"dependencies": {
"utilities": "1.0.0",
<...>
}
}
Error durante la implementación de la función:
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"}}
Las funciones funcionan bien localmente en el emulador.
Subiendo node_modules
(usando functions.ignore
en _firebase.json_). El resultado es el mismo.
Supongo que es porque utilities
se crea como syslink en _node-modules_ node_modules/utilities -> ../../utilities
¿Podría ser que firebase-tools no incluye el contenido de los módulos con enlaces simbólicos al cargar (sin desreferenciar)?
Lo siento, ¿podría aclarar en qué carpeta vive su firebase.json
(y mostrar su sección de configuración para functions
)?
_firebase.json_ estaba en la carpeta raíz. La configuración era estándar. Algo así:
"functions": {
"predeploy": [
"yarn --cwd \"$RESOURCE_DIR\" run lint",
"yarn --cwd \"$RESOURCE_DIR\" run build"
],
"source": "functions",
"ignore": []
},
<...>
todo se implementó como se esperaba (incluido _node_modules_) excepto node_modules/utilities
que es un enlace simbólico.
Me las arreglo para solucionar este problema escribiendo algunos scripts que:
yarn pack
). por ejemplo, esto crea _utilities.tgz_.dependencies { "utilities": "1.0.0"
-> dependencies { "utilities": "file:./utilities.tgz"
salida del contenido del directorio antes de la carga:
- dist
| - lib
| | -index.js
| - utilities.tgz
| - package.json <---------- This is modified to use *.tgz for workspaces
@audkar Hoy me encontré con el mismo problema que tú.
Soy nuevo en los espacios de trabajo de Lerna y Yarn. Según tengo entendido, también puedes usar Lerna. ¿Eso ayudaría de alguna manera?
Tu solución me parece un poco complicada 🤔
También se pregunta, ¿para qué sirve `--cwd" $ RESOURCE_DIR "?
--cwd
significa "directorio de trabajo actual" y $RESOURCE_DIR
contiene el valor del directorio fuente ( functions
en este caso). Agregar esta bandera hará que yarn
se ejecute en functions
dir en lugar de root
@audkar Ah, ya veo. Entonces podrías hacer lo mismo con yarn workspace functions lint
y yarn workspace functions build
@dinvlad No me queda claro por qué está apuntando a la carpeta dist y copiando cosas allí. Si construye para dist, pero deja el package.json donde está y apunta main a dist/index.js
entonces las cosas deberían funcionar igual, ¿no? Luego, debe establecer source en api en lugar de api / dist.
@dinvlad Aprendí el yarn workspace
de tus comentarios, pero parece que no puedo hacerlo funcionar por alguna razón. Vea aquí . ¿Alguna idea?
Perdón por haberme salido un poco del tema. Tal vez comente en SO, para minimizar el ruido.
@ 0x80 Copio package.json
a api/dist
y apunto firebase.json
a api/dist
para que solo los archivos "construidos" estén empaquetados dentro de la función de nube. No estoy seguro de qué pasará si apunto firebase.json
a api
; tal vez aún sea lo suficientemente inteligente como para empaquetar solo lo que hay dentro api/dist
(basado en main
atributo en package.json
). Pero pensé que era más limpio señalar solo api/dist
.
Re yarn workspace
, respondí SO ;)
@dinvlad agrupará la raíz de aquello a lo que lo
Ahora he usado una solución similar a @audkar.
{
"functions": {
"source": "packages/cloud-functions",
"predeploy": ["./scripts/pre-deploy-cloud-functions"],
"ignore": [
"src",
"node_modules"
]
}
}
Entonces el script pre-deploy-cloud-functions es:
#!/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
Y packages / cloud-functions tiene un archivo gitignore adicional:
yarn.lock
*.tgz
esto es lo que funcionó para mí
- root/
| - .firebaserc
| - firebase.json
- packages/
| - package1/
| - functions/
| - dist/
| - src/
| packages.json
y en el root/firebase.json
:
''
{
"funciones": {
"predeploy": "npm --prefix" $ RESOURCE_DIR "ejecutar compilación",
"fuente": "paquetes / funciones"
}
}
`` ``
@kaminskypavel, ¿sus paquetes / funciones dependen de los paquetes / paquete1 (o de algún otro paquete hermano)?
@ 0x80 positivo.
Creo que hubo algo fundamental que entendí mal sobre monorepos. Supuse que puede compartir un paquete e implementar una aplicación usando ese paquete sin publicar el paquete compartido en NPM.
Parece que esto no es posible, porque implementaciones como Firebase o Now.sh generalmente cargan el código y luego en la nube hacen una instalación y compilación. ¿Estoy en lo correcto?
@kaminskypavel Probé su enfoque y funciona, pero solo después de publicar mi paquete en NPM primero. Debido a que, en mi caso, el paquete es privado, inicialmente recibí un error de "no encontrado", por lo que también tuve que agregar mi archivo .npmrc a la raíz del paquete de funciones en la nube como se describe aquí.
@audkar ¿Está publicando su paquete común en NPM o es como yo tratando de implementar con código compartido que no está publicado?
@ 0x80 Estoy de acuerdo con usted en este entendimiento: creo que las implementaciones de la función Firebase están asumiendo (erróneamente) que todos los paquetes nombrados en package.json estarán disponibles en npm, en nombre de acelerar las implementaciones.
A medida que las configuraciones del espacio de trabajo de yarn se vuelven más populares, imagino que más personas se sorprenderán de que no puedan usar paquetes con enlaces simbólicos en Firebase Functions, especialmente porque funcionan bien hasta que se implementa.
Con npm agregando soporte para espacios de trabajo , tenemos un estándar de ecosistema sobre cómo deberían funcionar los paquetes locales.
Dado que este problema tiene más de un año, ¿alguna actualización del lado de Firebase sobre los planes (o falta de planes) aquí?
Creo que es una gran oportunidad: la variedad de servicios de Firebase pide una buena configuración de monorepo.
+1 en esto, las funciones en la nube generalmente necesitarán compartir algún código común (por ejemplo, interfaces) con otras aplicaciones y una buena manera de lidiar con esto es un monorepo (por ejemplo, lerna) o usando enlaces simbólicos directamente. Tomé este último y lo resolví creando algunos scripts. El concepto es bastante sencillo: copio lo que se necesita dentro del directorio de funciones y lo elimino después
Así es como lo hice con esta estructura de directorio:
''
content of `pre-deploy.js`
const fs = require ("fs-extra");
const packageJsonPath = "./package.json";
const packageJson = require (packageJsonPath);
(asíncrono () => {
aguardar fs.remove ( ./shared
);
aguardar 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");
(asíncrono () => {
aguardar 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)
"funciones": {
"fuente": "funciones",
"antes del despliegue": [
"npm --prefix" $ RESOURCE_DIR "ejecutar antes de la implementación"
],
"posterior a la implementación": [
"npm --prefix" $ RESOURCE_DIR "ejecutar después de la implementación"
]
},
''
Si realiza la compilación, dentro del directorio dist
o lib
ahora debería tener dos hermanos: funciones y compartidos (esto sucedió debido a la dependencia compartida). Asegúrese de actualizar las funciones package.json
main
para que apunten a lib/functions/src/index.js
para que la implementación funcione.
Por ahora está resuelto, pero es una solución alternativa, no una solución. Creo que las herramientas de base de fuego realmente deberían admitir enlaces simbólicos
@michelepatrassi inspirado en lo que me has firelink
para administrar este caso. Utiliza internamente rsync
para copiar archivos recursivos.
https://github.com/rxdi/firelink
npm i -g @rxdi/firelink
Uso básico
Suponiendo que tiene un enfoque de monorepo y sus paquetes están ubicados 2 niveles por debajo del directorio actual donde se encuentra package.json
.
package.json
"fireDependencies": {
"@graphql/database": "../../packages/database",
"@graphql/shared": "../../packages/shared",
"@graphql/introspection": "../../packages/introspection"
},
Al ejecutar firelink
, copiará los paquetes relacionados con las carpetas, luego mapeará los paquetes existentes con la instalación del módulo local "@graphql/database": "file:./.packages/database",
luego ejecutará el comando firebase
y pasará el resto de los argumentos de firelink
comando.
Básicamente, firelink
es un reemplazo de firebase
CLI ya que genera firebase
al final cuando termina su trabajo copiando packages
y modificando package.json
!
¡Saludos!
Nos acaba de morder esto, creo que monorepos se convertirá en el estándar y esto debería ser compatible de forma predeterminada.
Chicos, una solución simple para este problema es usar el paquete web para agrupar sus funciones en la nube e implementarlas desde el directorio incluido. Adjunto aquí hay un archivo de paquete web básico que estoy usando. Durante la compilación, esto empaquetará todo el código de funciones, incluidas las dependencias resueltas dentro del repositorio mono en una carpeta de nivel superior (en este caso, es webpack/cloud-functions
, podría ser cualquier cosa que configure)
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'
}
};
Y finalmente, en su archivo firebase.json
, consulte esta carpeta para la implementación.
{
"functions": {
"source": "webpack/cloud-functions"
}
}
Recuerde que debe tener un package.json
archivo en el webpack/cloud-functions
carpeta también.
{
"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
}
Esto está probado y funcionando. Estoy usando Google Cloud Build. Solicite más información si es necesario.
Gracias,
@sowdri ¡ Gracias por compartir! ¿Activa la compilación del paquete web desde el paso firebase.json functions.predeploy
, o la activa desde el script de compilación en la nube antes de llamar a la implementación de la base de fuego?
@ 0x80 Lo estoy construyendo en el paso cloud build
. Entonces, de acuerdo con firebase cli
, es solo un conjunto de funciones creadas con JS
@sowdri Gracias por el ejemplo. Terminamos usando el mismo enfoque en un proyecto anterior para AWS Lambdas / Serverless: era más fácil cargar un paquete (Webpack) que forzar a las herramientas a trabajar con el hilo monorepo.
También estoy atascado con esto ... todo funcionó bien (incluso el emulador de base de fuego) hasta que intenté implementar funciones (la compilación de la aplicación web en reaccionar se implementa bien). :( Esperemos que el equipo de Firebase pueda agregar soporte para monorepos pronto.
Utilizo babel para otros paquetes en mi monorepo, así que preferiría quedarme con eso. Si nada más, puedo probar el paquete web ... que parece que funciona para algunos.
EDITAR: Resolví esto usando el registro de paquetes de GitHub. Entonces publico todos mis paquetes allí y luego por travis
y functions
servidor. Tengo que configurar el registro y proporcionar un token para la autenticación (hecho a través de .npmrc
). ... parece una solución elegante. Tengo la idea de este enfoque aquí: https://medium.com/gdgeurope/how-to-use-firebase-cloud-functions-and-yarn-workspaces-24ca35e941eb
Sí, esto también me mordió. Todo funcionó perfectamente en firebase serve --only functions
pero cuando se implementó, no pudo ubicar un módulo.
Terminé construyendo un pequeño script para crear paquetes para mí. Si bien esto refleja detalles de mi entorno, como usar módulos y los nombres de mis paquetes que coincidan con los nombres de mi directorio, espero que sea útil para otros.
`` `importar fs desde" fs ";
importar child_process de "child_process";
const internalPackagesFull = new Map ();
// Encuentra todos los departamentos del paquete
const getDepsForPackage = (packageName) => {
const packageDir = packageName.split ("/") [1]; // ESTO PUEDE NECESITAR CAMBIAR PARA USTED
const packageSpecFileName = ../${packageDir}/package.json
;
const packageSpecFile = fs.readFileSync (packageSpecFileName);
const packageSpec = JSON.parse (packageSpecFile);
const packageInternalDeps = Object.keys (
packageSpec.dependencies
) .filter ((clave) => clave.includes ("turing")); // ESTO TENDRÁ QUE CAMBIAR PARA USTED
const packageTgzName = ${packageName.replace("@", "").replace("/", "-")}-v${
packageSpec.version
}.tgz
;
internalPackagesFull.set (packageName, {
packageSpecFileName,
packageSpec,
packageDir,
packageInternalDeps,
packageTgzName,
});
const packagesToProcess = packageInternalDeps.filter (
(internalDepName) =>! internalPackagesFull.has (internalDepName)
);
packagesToProcess.forEach ((internalPackageName) =>
getDepsForPackage (internalPackageName)
);
};
const packageName = JSON.parse (fs.readFileSync ("./ package.json")). nombre;
child_process.execSync ( cp ./package.json ./package.json.org
);
getDepsForPackage (nombrePaquete);
// escribe paquetes actualizados: usa js comunes y las referencias son archivos tgz locales
[... internalPackagesFull.values ()]. forEach ((internalDep) => {
const {packageSpec, packageSpecFileName, packageInternalDeps} = internalDep;
// cambia el tipo de paquete
packageSpec.type = "commonjs"; // ESTO PUEDE NECESITAR CAMBIAR PARA USTED
// especifica la ubicación del dep para ser el archivo zip empaquetado
packageInternalDeps.forEach ((internalDepOfPackage) => {
const {packageTgzName} = internalPackagesFull.get (internalDepOfPackage);
packageSpec.dependencies [internalDepOfPackage] = ./${packageTgzName}
;
});
fs.writeFileSync (
packageSpecFileName,
JSON.stringify (packageSpec, null, "")
);
});
// ejecutar la construcción y el embalaje de hilo
[... internalPackagesFull.values ()]. forEach ((internalDep) => {
tratar {
console.log ( Buliding ${internalDep.packageDir}
);
child_process.execSync ("construcción de hilo", {
cwd: ../${internalDep.packageDir}
,
});
console.log ( Packaging ${internalDep.packageDir}
);
child_process.execSync ("paquete de lanas", {
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: ".",
}
);
}
} captura (e) {
console.log (e);
}
});
// volver a la estructura de paquetes estándar
[... internalPackagesFull.values ()]
.filter ((internalDep) => packageName! == internalDep.packageSpec.name)
.forEach ((internalDep) => {
const {
packageSpec,
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, " ")
);
});
''
Usé la solución proporcionada por @sowdri (¡gracias por eso!), Con un pequeño ajuste para poder eliminar y regenerar libremente todo el directorio dist/
, incluido el package.json
secundario:
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
...,
plugins: [
new CopyPlugin({
patterns: [{ from: 'package.dist.json', to: 'package.json' }],
}),
],
};
Y luego guardo los siguientes package.dist.json
en la raíz del paquete:
{
"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
}
Podría ser posible eliminar las dependencias de este archivo y confiar en Webpack para agruparlas también (eliminando la necesidad de mantener estas dependencias sincronizadas con su package.json
), pero no lo he intentado.
Estoy usando un solo repositorio con un script para copiar .firebaserc
& firebase.json
para todos los directorios relevantes o proyectos de base de fuego usando el paquete npm
Creo package.json
del package.json original para la implementación de funciones.
aquí está el script 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);
})();
para la implementación, vaya a ./out/[project-name]/firebase deploy --only=functions
o escriba un script para él también.
Me encuentro con algunas advertencias de Webpack como esta:
ADVERTENCIA en /Users/me/Development/myproject/node_modules/firebase-functions/lib/config.js 61: 23-42
Dependencia crítica: la solicitud de una dependencia es una expresión
¿Encontraste una manera de resolverlos o los estás ignorando / suprimiendo?
Logré que todo funcionara sin advertencias 🥳 Terminé con la siguiente configuración. Especialmente el uso de patrones de expresiones regulares para los externos marcó la diferencia porque si solo tiene "firebase-functions" en sus externos, cualquier importación que realice desde un submódulo no coincidirá y la biblioteca seguirá incluida en su paquete.
Como resultado de los problemas de agrupación, también encontré algunos errores crípticos de @grpc durante la implementación. Olvidé guardarlos como referencia.
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" }],
}),
],
};
Resulta que no es necesario un package.dist.json por separado. Lo molesto de tener este archivo es, por supuesto, que debe actualizarlo manualmente cada vez que actualice cualquiera de las dependencias enumeradas allí. Entonces es muy fácil olvidar eso.
En su lugar, mueva todos los paquetes que _no_ debería_ enumerar en su package.dist.json a la lista devDependencies, y simplemente use ese archivo en el complemento de copia del paquete web. Ahora solo tienes un paquete.json con el que lidiar 🎉
Además, no quiero que mi versión local de nodejs sea necesariamente la misma que las funciones implementadas en la versión de nodo. Descubrí que ahora puede especificar functions.runtime
en el archivo firebase.json. Así que saque el campo engines
del package.json y en su lugar configure functions.runtime
en "10" o "12" en la configuración de su base de fuego.
Esto es lo que parece para mí:
{
"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
}
}
}
¿Cómo maneja los mapas de origen? Si agrupo mis funciones con el paquete web usando devtool: "inline-source-map"
, no se recogen en el informe de errores de stackdriver. @sowdri
Me encontré con lo mismo en mi proyecto y esto fue un fastidio bastante grande ya que no quiero publicar todos los paquetes para un proyecto paralelo. Era molesto mantenerse al día con varios archivos package.json
o tener que compilar fuera del paquete. Resulta que si incluye sus departamentos como opcionales, Lerna aún los recogerá y Firebase no se quejará cuando se cargue.
¡La siguiente configuración le brindará soporte para dep con enlaces simbólicos con un solo 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
}
}
}
Estoy usando espacios de trabajo de hilo, pero tampoco necesito mencionar los nombres de mis paquetes locales en otros archivos package.json. Estoy implementando con éxito tanto en Firebase como en Vercel sin él.
No estoy seguro de qué lo hace funcionar. Solo tenga esta configuración estándar en mi package.json de nivel superior:
"workspaces": {
"packages": [
"packages/*"
]
},
No me importa tener un package.json en cada una de las carpetas / packages / *, ya que ejecutar yarn upgrade-interactive
solo los manejará todos de una vez. En algunos paquetes, encuentro útil poder agregar un script específicamente para ese alcance.
---- editar ----
Olvidé mencionar que estoy usando TypeScript con referencias de proyectos. Eso podría tener algo que ver con eso. Para Vercel, estoy usando next-transpile-modules para incluir mi código compartido en el paquete.
Estoy usando una configuración similar a @ 0x80 y @sowdri para que esto funcione, pero en lugar de mover mis dependencias locales a devDependencies (que en realidad no funcionó para mí, creo que me faltaba un paso) y uso copy-webpack-plugin
, Estoy usando generate-package-json-webpack-plugin
para construir mi package.json en la carpeta dist. Esto crea mi lista de dependencias a partir de lo que realmente requiere el código resultante, por lo que si está incluido o es una dependencia de desarrollo, no se incluye en el package.json resultante.
También configuré mis externos usando webpack-node-externals
para hacer que todo sea externo, excepto mis paquetes monorepo enlazados simbólicamente, que estoy usando regex para que coincida con el prefijo del nombre del proyecto. También agregué las expresiones regex para los paquetes firebase @ 0x80 publicados como externos adicionales.
Esta es mi configuración
/* 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)]
};
El otro beneficio de usar webpack para empaquetar el código es que finalmente puedo usar alias de módulo :)
Comentario más útil
+1 en esto, las funciones en la nube generalmente necesitarán compartir algún código común (por ejemplo, interfaces) con otras aplicaciones y una buena manera de lidiar con esto es un monorepo (por ejemplo, lerna) o usando enlaces simbólicos directamente. Tomé este último y lo resolví creando algunos scripts. El concepto es bastante sencillo: copio lo que se necesita dentro del directorio de funciones y lo elimino después
Así es como lo hice con esta estructura de directorio:
''
| - .firebaserc
| - firebase.json
| - ...
| - src /
| - paquete.json
| - pre-deploy.js
| - post-deploy.js
| - ....
| - src /
| - paquete.json
| - ....
const fs = require ("fs-extra");
const packageJsonPath = "./package.json";
const packageJson = require (packageJsonPath);
(asíncrono () => {
aguardar fs.remove (
./shared
);aguardar fs.copy (
../shared
,./shared
);}) ();
const packageJsonPath = "./package.json";
const packageJson = require (packageJsonPath);
const fs = require ("fs-extra");
(asíncrono () => {
aguardar fs.remove (
./shared
);}) ();
"funciones": {
"fuente": "funciones",
"antes del despliegue": [
"npm --prefix" $ RESOURCE_DIR "ejecutar antes de la implementación"
],
"posterior a la implementación": [
"npm --prefix" $ RESOURCE_DIR "ejecutar después de la implementación"
]
},
''
Si realiza la compilación, dentro del directorio
dist
olib
ahora debería tener dos hermanos: funciones y compartidos (esto sucedió debido a la dependencia compartida). Asegúrese de actualizar las funcionespackage.json
main
para que apunten alib/functions/src/index.js
para que la implementación funcione.Por ahora está resuelto, pero es una solución alternativa, no una solución. Creo que las herramientas de base de fuego realmente deberían admitir enlaces simbólicos