Firebase-tools: Поддержка моно-репозиториев при развертывании

Созданный на 31 янв. 2018  ·  47Комментарии  ·  Источник: firebase/firebase-tools

См. Https://github.com/firebase/firebase-functions/issues/172

Информация о версии

3.17.3

Действия по воспроизведению

Ожидаемое поведение

Фактическое поведение

feature request

Самый полезный комментарий

+1 к этому, облачные функции обычно должны будут совместно использовать некоторый общий код (например, интерфейсы) с другими приложениями, и хороший способ справиться с этим - монорепорация (например, lerna) или прямое использование символических ссылок. Я взял последнее и решил, создав несколько скриптов. Идея довольно проста: я копирую то, что нужно, в каталог функций, и удаляю его после

Вот как я сделал это с этой структурой каталогов:
`` ''

  • корень/
    | - .firebaserc
    | - firebase.json
    | - ...
  • функции /
    | - src /
    | - package.json
    | - pre-deploy.js
    | - post-deploy.js
    | - ....
  • общий/
    | - src /
    | - package.json
    | - ....
content of `pre-deploy.js`

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

const packageJsonPath = "./package.json";
const packageJson = требуется (packageJsonPath);

(async () => {
ждать fs.remove ( ./shared );
await 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 = require ("fs-extra");

(async () => {
ждать 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)

"functions": {
"источник": "функции",
"Preploy": [
"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"
  },
  ...

Итак, 2 уровня вложенности по-прежнему успешно разрешают корень 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 не выбирают зависимости символических ссылок при загрузке функций.

Ах, честно, я сам избежал этой кроличьей норы

Не могли бы вы пояснить, в чем проблема? Как упоминалось выше, я просто использую чистый 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",

К сожалению, я до сих пор не могу поделиться полным кодом, но важна только эта настройка, черт возьми.

Кроме того, я могу ошибаться, но я думаю, что сценарий firebase deploy самом деле не использует ваш каталог node_modules . Я думаю, он просто берет код, package.json и yarn.lock из каталогов dist и делает все остальное.

Это правда. Значение по умолчанию для functions.ignore в firebase.json -
["node_modules"], поэтому он не загружен. Я верю, что ты можешь это преодолеть
хотя, если вы хотите отправить некоторые локальные модули.

В пн, 17.06.2019, 18:58 Денис Логинов [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=ACATB2U73VS2KIILUVRFFB3P3P3A6NPA5CNFSM4EOR24GKYY3PNVWWK3TREBORM4EOR24GKYY3PNVWWK3TREFWXG4DVMWWWK3TREXW2X4DFVMXWWMXWWMXX4DFVMXWWMXX4DFWMXWWM6
или отключить поток
https://github.com/notifications/unsubscribe-auth/ACATB2U3Q2TLVBICRJ3B5OLP3A6NPANCNFSM4EOR24GA
.

@dinvlad Да, для этого требуется package.json и любой файл блокировки, который вы используете при установке deps в развертывании облачной почты.

Я считаю, что сценарий, изначально описанный в другом выпуске, заключался в использовании общего пакета в рабочей области и некоторых проблемах с поднятием области видимости. Поскольку я не использовал пряжу таким образом, я могу только предполагать, исходя из того, что я там прочитал.

@samtstern @jthegedus спасибо, приятно знать!

Кажется, мы все говорим о разных проблемах. Постараюсь описать 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-tools не включает содержимое модулей с символическими ссылками при загрузке (без разыменования)?

Извините, не могли бы вы уточнить, в какой папке находится ваш 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"
  • развертывание этого каталога в firebase

вывести содержимое каталога перед загрузкой:

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

@audkar Сегодня я столкнулся с той же проблемой, что и вы.

Я новичок в рабочих пространствах Lerna и Yarn. Насколько я понимаю, можно просто использовать Лерну. Это хоть как-то поможет?

Ваш обходной путь кажется мне немного сложным 🤔

Также интересно, для чего нужен `--cwd" $ RESOURCE_DIR "?

--cwd означает «текущий рабочий каталог», а $RESOURCE_DIR содержит значение для исходного каталога (в данном случае functions ). Добавление этого флага заставит yarn выполняться в functions dir вместо root

@audkar А, понятно. Таким образом, вы можете сделать то же самое с yarn workspace functions lint и yarn workspace functions build

@dinvlad Мне непонятно, почему вы нацеливаетесь на папку dist и копируете туда вещи. Если вы выполните сборку на dist, но оставите package.json там, где он есть, и укажете main на 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 он

Теперь я использовал аналогичный обходной путь для @audkar.

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

Тогда сценарий pre-deploy-cloud-functions следующий:

#!/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 :
`` ''
{
"functions": {
"predploy": "npm --prefix" $ RESOURCE_DIR "run build",
"источник": "пакеты / функции"
}
}
`` ''

@kaminskypavel - это ваши пакеты / функции в зависимости от пакетов / package1 (или какого-либо другого родственного пакета)?

@ 0x80 положительный.

Я думаю, что было что-то фундаментальное, что я неправильно понял в монорепозитории. Я предполагал, что вы можете поделиться пакетом и развернуть приложение, используя этот пакет, без фактической публикации общего пакета в NPM.

Кажется, что это невозможно, потому что развертывания, такие как Firebase или Now.sh, обычно загружают код, а затем в облаке выполняют установку и сборку. Я прав?

@kaminskypavel Я попробовал ваш подход, и он работает, но только после публикации моего пакета в NPM. Поскольку в моем случае пакет является частным, я изначально получил ошибку «не найден», поэтому мне также пришлось добавить свой файл .npmrc в корень пакета облачных функций, как описано здесь.

@audkar Вы публикуете свой общий пакет в NPM, или вы, как и я, пытаетесь развернуть с общим кодом, который не опубликован?

@ 0x80 Я

Поскольку настройки рабочего пространства yarn становятся все более популярными, я полагаю, что все больше людей будут удивлены тем, что они не могут использовать пакеты с символическими ссылками в функциях Firebase - тем более, что они отлично работают до тех пор, пока вы не развернете.

Благодаря добавлению поддержки рабочих областей в npm у нас появился стандарт экосистемы, определяющий, как должны работать локальные пакеты.

Поскольку этой проблеме больше года, есть ли здесь какие-либо обновления со стороны Firebase о планах (или отсутствии планов)?

Я думаю, что это довольно крутая возможность - множество сервисов Firebase требует хорошей настройки монорепозитория.

+1 к этому, облачные функции обычно должны будут совместно использовать некоторый общий код (например, интерфейсы) с другими приложениями, и хороший способ справиться с этим - монорепорация (например, lerna) или прямое использование символических ссылок. Я взял последнее и решил, создав несколько скриптов. Идея довольно проста: я копирую то, что нужно, в каталог функций, и удаляю его после

Вот как я сделал это с этой структурой каталогов:
`` ''

  • корень/
    | - .firebaserc
    | - firebase.json
    | - ...
  • функции /
    | - src /
    | - package.json
    | - pre-deploy.js
    | - post-deploy.js
    | - ....
  • общий/
    | - src /
    | - package.json
    | - ....
content of `pre-deploy.js`

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

const packageJsonPath = "./package.json";
const packageJson = требуется (packageJsonPath);

(async () => {
ждать fs.remove ( ./shared );
await 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 = require ("fs-extra");

(async () => {
ждать 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)

"functions": {
"источник": "функции",
"Preploy": [
"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, и ваши пакеты расположены на 2 уровня ниже текущего каталога, в котором находится package.json .

package.json

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

При выполнении firelink он скопирует пакеты, связанные с папками, затем сопоставит существующие пакеты с локальным модулем install "@graphql/database": "file:./.packages/database", затем выполнит команду firebase и передаст остальные аргументы из firelink команда.
По сути, firelink - это замена firebase CLI, поскольку он порождает firebase в конце, когда завершает свою работу, копируя packages и изменяя package.json !

С Уважением!

Нас это только что укусило, я думаю, что монорепозитории станут стандартом, и это должно поддерживаться по умолчанию.

Ребята, простое решение этой проблемы - использовать webpack для объединения ваших облачных функций и развертывания из связанного каталога. К этому приложению прилагается базовый файл веб-пакета, который я использую. Во время сборки он упакует весь код функций, включая зависимости, разрешенные в монорепозитории, в папку верхнего уровня (в данном случае это 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
}

Это проверено и работает. Я использую сборку Google Cloud. При необходимости запросите дополнительную информацию.

Спасибо,

@sowdri Спасибо, что поделились! Запускаете ли вы сборку веб-пакета из шага firebase.json functions.predeploy или запускаете его из сценария сборки облака перед вызовом развертывания firebase?

@ 0x80 Я создаю его на шаге cloud build . Итак, согласно firebase cli это просто набор функций, созданных с помощью JS.

@sowdri Спасибо за пример. Мы закончили тем, что использовали тот же подход в прошлом проекте для AWS Lambdas / Serverless: было проще загрузить пакет (Webpack), чем заставить инструменты работать с yarn monorepo.

Я тоже застрял в этом .... все работало нормально (даже эмулятор firebase), пока я не попытался развернуть функции (сборка веб-приложения в реакции развертывается нормально). :( Надеюсь, команда firebase скоро добавит поддержку монорепозиториев.

Я использую babel для других пакетов в моем монорепозитории, поэтому я бы предпочел остаться с ним. Если ничего другого, я могу попробовать веб-пакет ... который, похоже, для некоторых работает.

РЕДАКТИРОВАТЬ: Я решил это с помощью реестра пакетов GitHub. Поэтому я публикую все свои пакеты здесь, а затем для сервера travis и functions Мне нужно настроить реестр и предоставить токен для аутентификации (выполняется через .npmrc ). ... кажется элегантным решением. Идея этого подхода у меня появилась здесь: https://medium.com/gdgeurope/how-to-use-firebase-cloud-functions-and-yarn-workspaces-24ca35e941eb

Да, это тоже укусило. Все работало отлично в firebase serve --only functions но при развертывании не смог найти модуль.

В итоге я написал небольшой скрипт для сборки пакетов для меня. Хотя это отражает особенности моей среды, такие как использование модулей и имен моих пакетов, совпадающих с именами моих каталогов, я надеюсь, что это будет полезно для других.

`` импортировать фс из фс;
импортировать child_process из "child_process";

const internalPackagesFull = новая карта ();

// Находим все значения для пакета
const getDepsForPackage = (packageName) => {
const packageDir = packageName.split ("/") [1]; // МОЖЕТ НЕОБХОДИМО ИЗМЕНИТЬ ДЛЯ ВАС
const packageSpecFileName = ../${packageDir}/package.json ;
const packageSpecFile = fs.readFileSync (packageSpecFileName);
const packageSpec = JSON.parse (packageSpecFile);
const packageInternalDeps = Object.keys (
packageSpec.dependencies
) .filter ((ключ) => key.includes ("тьюринг")); // ЭТО НЕОБХОДИМО ИЗМЕНИТЬ ДЛЯ ВАС

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

internalPackagesFull.set (имя_пакета, {
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")). 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"; // МОЖЕТ НЕОБХОДИМО ИЗМЕНИТЬ ДЛЯ ВАС

// указываем расположение депа, который будет упакованным zip-файлом
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 (e);
}
});

// возвращаемся к стандартной структуре пакетов
[... 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, "  ")
);

});
`` ''

Я использовал решение, предоставленное @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 из исходного 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
Критическая зависимость: запрос зависимости - это выражение

Вы нашли способ решить эти проблемы или игнорируете / подавляете их?

Мне удалось заставить все работать без предупреждений 🥳 Я получил конфигурацию ниже. В частности, использование шаблонов регулярных выражений для внешних элементов имело значение, потому что, если у вас есть просто «firebase-functions» во внешних элементах, любой импорт, который вы делаете из подмодуля, не будет сопоставлен, и библиотека все равно будет включена в ваш пакет.

В результате проблем со сборкой я также столкнулся с некоторыми загадочными ошибками

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

Оказывается, отдельный package.dist.json не нужен. Раздражает наличие этого файла, конечно, то, что вам нужно вручную обновлять его каждый раз, когда вы обновляете любую из зависимостей, перечисленных в нем. Так что об этом очень легко забыть.

Вместо этого переместите все пакеты, которые вы _would not_ перечислить в вашем package.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. @sowdri

Я столкнулся с тем же самым в своем проекте, и это был довольно большой облом, так как я не хочу публиковать каждый пакет для побочного проекта. Было неприятно работать с несколькими файлами package.json или создавать вне пакета. Оказывается, если вы включите свои deps в качестве необязательных, 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
    }
  }
}

Я использую рабочие области yarn, но мне также не нужно упоминать имена моих локальных пакетов в других файлах package.json. Я успешно развертываю и Firebase, и Vercel без него.

Я не уверен, что заставляет его работать. Просто добавьте эту стандартную конфигурацию в мой пакет верхнего уровня package.json:

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

Я не против иметь package.json в каждой из папок / packages / *, поскольку запуск yarn upgrade-interactive просто обработает их все за один раз. В некоторых пакетах я считаю полезным добавить скрипт специально для этой области.

---- редактировать ----

Я забыл упомянуть, что использую Typescript со ссылками на проекты. Возможно, это как-то связано с этим. Для Vercel я использую next-transpile-modules, чтобы включить свой общий код в пакет.

Я использую аналогичную настройку для @ 0x80 и @sowdri, чтобы заставить эту работу, но вместо того, чтобы перемещать мои локальные зависимости в devDependencies (что на самом деле не сработало для меня, думаю, я пропустил шаг) и использую copy-webpack-plugin , Я использую generate-package-json-webpack-plugin для создания моего package.json в папке dist. Это строит мой список зависимостей из того, что на самом деле требует результирующий код, поэтому, если он связан или является зависимостью разработчика, он не включается в результирующий package.json.

Я также настроил свои внешние элементы, используя webpack-node-externals чтобы сделать все внешнее, за исключением моих пакетов монорепозитория с символическими ссылками, которые я использую регулярное выражение для соответствия префиксу имени проекта. Я также добавил регулярные выражения для пакетов 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)]
};

Еще одно преимущество использования webpack для объединения кода - я наконец могу использовать псевдонимы модулей :)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги