Використання спільного модуля вузла для загальних класів


15

Мета

Тож у мене є проект із цією структурою:

  • іонно-додаток
  • вогнева база-функції
  • спільний

Мета - визначити загальні інтерфейси та класи в sharedмодулі.

Обмеження

Я не хочу завантажувати свій код у npm, щоб використовувати його локально, і взагалі не планую завантажувати код. Він повинен на 100% працювати в режимі офлайн.

Хоча процес розвитку повинен працювати в автономному режимі, ionic-appі firebase-functionsмодулі будуть розгорнуті firebase (хостинг і функція). Тому код з sharedмодуля повинен бути там доступний.

Що я спробував поки що

  • Я спробував використовувати посилання на проект у машинописі, але я не наблизив його до роботи
  • Я спробував це встановити як модуль npm, як у другій відповіді на це питання
    • Спочатку, здається, працює нормально, але під час збирання я отримую помилку під час запуску firebase deploy:
Function failed on loading user code. Error message: Code in file lib/index.js can't be loaded.
Did you list all required modules in the package.json dependencies?
Detailed stack trace: Error: Cannot find module 'shared'
    at Function.Module._resolveFilename (module.js:548:15)
    at Function.Module._load (module.js:475:25)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/srv/lib/index.js:5:18)

Питання

Чи є у вас рішення для створення спільного модуля, використовуючи конфігурації типів друку або NPM?

Будь ласка, не позначайте це як дублікат → Я спробував будь-яке рішення, знайдене в StackOverflow.

Додаткова інформація

Налаштування для спільного використання:

// package.json
{
  "name": "shared",
  "version": "1.0.0",
  "description": "",
  "main": "dist/src/index.js",
  "types": "dist/src/index.d.ts",
  "files": [
    "dist/src/**/*"
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "publishConfig": {
    "access": "private"
  }
}

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "rootDir": ".",
    "sourceRoot": "src",
    "outDir": "dist",
    "sourceMap": true,
    "declaration": true,
    "target": "es2017"
  }
}

Налаштування функцій:

// package.json
{
  "name": "functions",
  "scripts": {
    "lint": "tslint --project tsconfig.json",
    "build": "tsc",
    "serve": "npm run build && firebase serve --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "8"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "^8.0.0",
    "firebase-functions": "^3.1.0",
    "shared": "file:../../shared"
  },
  "devDependencies": {
    "@types/braintree": "^2.20.0",
    "tslint": "^5.12.0",
    "typescript": "^3.2.2"
  },
  "private": true
}


// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./",
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": false,
    "rootDir": "src",
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017"
  }
}

Поточний сутінг

Я додав скрипт npm до спільного модуля, який копіює всі файли (без index.js) на інші модулі. У цьому проблема, що я перевіряю дублікат коду в SCM, і що мені потрібно запускати цю команду при кожній зміні. Також IDE просто розглядає його як різні файли.

Відповіді:


4

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

Припускаючи таку структуру каталогу:

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

Під час розгортання служби Firebase ви можете прикріпити команди до гаків попереднього вибору та післявикористання . Це робиться за firebase.jsonдопомогою властивостей predeployта postdeployна потрібній службі. Ці властивості містять масив послідовних команд, які потрібно виконати до та після розгортання коду відповідно. Крім того, ці команди викликаються змінними середовища RESOURCE_DIR(шлях до каталогу ./functionsабо ./ionic-app, залежно від того, що застосовується) та PROJECT_DIR(шлях до каталогу firebase.json).

Використовуючи predeployмасив для functionsвсередині firebase.json, ми можемо скопіювати код спільної бібліотеки у папку, яка розгорнута в екземпляр Cloud Functions. Роблячи це, ви можете просто включити спільний код так, як ніби це бібліотека, розташована в підпапці, або ви можете зіставити його ім'я, використовуючи відображення шляху Typescript у tsconfig.jsonназваний модуль (так що ви можете використовувати import { hiThere } from 'shared';).

Визначення predeployгака (використовується глобальна установка shxсумісності для Windows):

// firebase.json
{
  "functions": {
    "predeploy": [
      "shx rm -rf \"$RESOURCE_DIR/src/shared\"", // delete existing files
      "shx cp -R \"$PROJECT_DIR/shared/.\" \"$RESOURCE_DIR/src/shared\"", // copy latest version
      "npm --prefix \"$RESOURCE_DIR\" run lint", // lint & compile
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ]
  },
  "hosting": {
    "public": "ionic-app",
    ...
  }
}

Пов’язання джерела скопійованої бібліотеки скопійованого файлу з конфігурацією компілятора typecript функції:

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

Пов’язання імені модуля, "спільного", в папку скопійованої бібліотеки.

// functions/package.json
{
  "name": "functions",
  "scripts": {
    ...
  },
  "engines": {
    "node": "8"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "^8.6.0",
    "firebase-functions": "^3.3.0",
    "shared": "file:./src/shared",
    ...
  },
  "devDependencies": {
    "tslint": "^5.12.0",
    "typescript": "^3.2.2",
    "firebase-functions-test": "^0.1.6"
  },
  "private": true
}

Такий же підхід можна використовувати і з папкою хостингу.


Сподіваємось, це надихає того, хто більше знайомий з компіляцією Typescript, придумати більш чисте рішення, яке використовує ці гачки.


3

Ви можете спробувати Lerna , інструмент для управління проектами JavaScript (і TypeScript) з декількома пакетами.

Налаштування

Якщо ваш проект має таку структуру каталогів:

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

Обов’язково вкажіть правильний рівень доступу ( privateта config/accessклавіші) у всіх модулях, які ви не бажаєте опублікувати, а також typingsзапис у своєму sharedмодулі:

Спільний доступ:

{
  "name": "shared",
  "version": "1.0.0",
  "private": true,
  "config": {
    "access": "private"
  },
  "main": "lib/index.js",
  "typings": "lib/index.d.ts",
  "scripts": {
    "compile": "tsc --project tsconfig.json"
  }
}

Додаток Ionic:

{
  "name": "ionic-app",
  "version": "1.0.0",
  "private": true,
  "config": {
    "access": "private"
  },
  "main": "lib/index.js",
  "scripts": {
    "compile": "tsc --project tsconfig.json"
  },
  "dependencies": {
    "shared": "1.0.0"
  }
}

Маючи вищезазначені зміни, ви можете створити кореневий рівень, package.jsonде ви можете вказати будь- devDependenciesякий, до якого хочете, щоб усі ваші модулі проекту мали доступ, наприклад, ваш блок тестування блоку, tslint тощо.

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

Ви також можете скористатися цим кореневим рівнем package.jsonдля визначення npm-скриптів, які викликатимуть відповідні сценарії в модулях вашого проекту (через lerna):

{
  "name": "my-project",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "compile": "lerna run compile --stream",
    "postinstall": "lerna bootstrap",
  },
  "devDependencies": {
    "lerna": "^3.18.4",
    "tslint": "^5.20.1",
    "typescript": "^3.7.2"
  },
}

З цим на місці додайте файл конфігурації lerna у свій кореневий каталог:

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

із наступним змістом:

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

Тепер, коли ви запустите npm installв кореневій директорії, postinstallсценарій, визначений у вашому кореневому рівні, package.jsonбуде викликати lerna bootstrap.

Що lerna bootstrapозначає, що він буде символізувати ваш sharedмодуль ionic-app/node_modules/sharedі firebase-functions/node_modules/shared, з точки зору цих двох модулів, sharedвиглядає так само, як і будь-який інший модуль npm.

Компіляція

Звичайно, поєднання модулів недостатньо, оскільки вам все одно потрібно компілювати їх з TypeScript в JavaScript.

Ось тут і package.json compileграє сценарій кореневого рівня .

При запуску npm run compileв кореневому каталозі проекту, НПЙ викликатиме lerna run compile --stream, і lerna run compile --streamвикликає скрипт compileв кожному з ваших модулів package.jsonфайлу.

Оскільки кожен з ваших модулів має свій власний compileсценарій, ви повинні мати tsonfig.jsonфайл на модуль. Якщо вам не подобається дублювання, ви можете піти з кореневим рівнем tsconfig або комбінацією файлів tsconfig кореневого рівня та модулів tsconfig на рівні модулів, що успадковуються від кореневого.

Якщо ви хочете побачити, як працює ця програма на реальному проекті, погляньте на Serenity / JS, де я досить широко її використовую.

Розгортання

Приємно, що sharedмодуль символізується node_modulesпід firebase-functionsі підionic-app , а ваш devDepedenciesпід node_modulesпід корінь проекту - це те, що якщо вам потрібно розгорнути споживчий модуль куди завгодно (так, ionic-appнаприклад,), ви можете просто застебнути його разом зі своїм node_modulesі не турбуватися про перед тим, як розгортати, потрібно видалити механізми розробки.

Сподіваюся, це допомагає!

Січ


Цікаво! Я точно перевірю це і подивлюсь, чи це правильно.
MauriceNino

2

Іншим можливим рішенням, якщо ви використовуєте git для управління кодом, використовується git submodule. Використанняgit submodule вас ви можете включити ще один сховище git у свій проект.

Застосовано до вашої справи використання:

  1. Натисніть на поточну версію вашого спільного git-сховища
  2. Використовуйте git submodule add <shared-git-repository-link>всередині своїх основних проектів, щоб зв’язати спільне сховище.

Ось посилання на документацію: https://git-scm.com/docs/git-submodule


Це насправді не погана ідея, але локальний розвиток та тестування в основному проходить при такому підході.
MauriceNino

0

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

Підхід 1: Місцеві копії

Ви можете використовувати Gulp для автоматизації вже описаного робочого рішення, але IMO підтримувати не дуже просто і різко збільшує складність, якщо в якийсь момент прийде інший розробник.

Підхід 2: Монорепо

Ви можете створити єдине сховище, яке містить усі три папки, і з'єднати їх, щоб вони поводилися як єдиний проект. Як вже було сказано вище, ви можете використовувати Lerna . Це вимагає трохи конфігурації, але коли вони будуть виконані, ці папки будуть вести себе як єдиний проект.

Підхід 3: компоненти

Розглядайте кожну з цих папок як окремий компонент. Погляньте на Біт . Це дозволить вам налаштувати папки як менші частини більшого проекту, і ви можете створити приватний обліковий запис, який охоплюватиме ці компоненти лише вам. Після початкового налаштування це дозволить вам навіть застосувати оновлення до окремих папок, і батьківська, яка їх використовує, автоматично отримає оновлення.

Підхід 4: пакети

Ви спеціально сказали, що не хочете використовувати npm, але я хочу поділитися ним, тому що я зараз працюю з набором, як описано нижче, і виконує ідеальну роботу для мене:

  1. Скористайтеся npmабо yarnстворіть пакет для кожної папки (ви можете створити масштабовані пакети для обох, щоб код був доступний лише вам, якщо це стосується вас).
  2. У батьківській папці (в якій використовуються всі ці папки) створені пакети підключені як залежності.
  3. Я використовую webpack для поєднання всього коду, використовуючи псевдоніми веб-упаковки в поєднанні з шляхами typecript.

Працює як шарм, і коли пакети пов'язані для місцевого розвитку, він працює повністю в автономному режимі, і на мій досвід - кожна папка може бути масштабованою окремо і дуже проста в обслуговуванні.

Примітка

У моєму випадку пакети "дочірні" вже попередньо складені, оскільки вони досить великі, і я створив окремі tsconfigs для кожного пакету, але чудове те, що ви можете легко змінити його. У минулому я обидва використовував машинопис в модулі і компілював файли, а також сирі файли js, тому вся справа дуже, дуже універсальна.

Сподіваюсь, це допомагає

***** ОНОВЛЕННЯ **** Продовжити на пункт 4: Прошу вибачення, погано. Можливо, я помилився, бо, наскільки я знаю, ви не можете символізувати модуль, якщо він не завантажений. Тим не менш, ось це:

  1. У вас є окремий модуль npm, давайте використовувати firebase-functions для цього. Ви складаєте його або використовуєте сирий ts, залежно від ваших уподобань.
  2. У своєму батьківському проекті додайте firebase-functions як залежність.
  3. В tsconfig.json, дод"paths": {"firebase-functions: ['node_modules/firebase-functions']"}
  4. У вебпаку - resolve: {extensions: ['ts', 'js'], alias: 'firebase-functions': }

Таким чином, ви посилаєтесь на всі експортовані функції з firebase-functionsмодуля просто за допомогою import { Something } from 'firebase-functions'. Webpack і TypeScript зв'язать його з папкою модулів вузлів. При такій конфігурації батьківський проект не буде перейматися, якщоfirebase-functions модуль написаний у TypeScript або ванільному javascript.

Після налаштування він буде чудово працювати для виробництва. Потім, щоб зв’язатися та працювати офлайн:

  1. Перейдіть до firebase-functionsпроекту та напишітьnpm link . Він створить локальне для вашої машини символьне посилання та відобразить ім'я, яке ви вказали в package.json.
  2. Перейдіть до батьківського проекту та напишіть npm link firebase-functions, що створить симпосилання та відобразить залежність функцій firebase до створеної вами папки.

Я думаю, ти щось неправильно зрозумів. Я ніколи не казав, що не хочу використовувати npm. Насправді всі три з цих модулів є модулями вузлів. Я щойно сказав, що не хочу завантажувати свої модулі в npm. Чи можете ви детальніше зупинитися на цій 4-й частині - це звучить цікаво? може надати зразок коду?
MauriceNino

Додам ще одну відповідь, оскільки це буде довгим і нечитабельним як коментар
Іван

Оновлено мою початкову відповідь, сподіваюсь, що це буде зрозуміліше
Іван

0

Я не хочу завантажувати свій код у npm, щоб використовувати його локально, і взагалі не планую завантажувати код. Він повинен на 100% працювати в режимі офлайн.

Усі модулі npm встановлюються локально і завжди працюють в автономному режимі, але якщо ви не хочете публікувати свої пакети публічно, щоб люди могли його бачити, ви можете встановити приватний реєстр npm.

ProGet - це сервер приватного сховища NuGet / Npm, доступний для Windows, який ви можете використовувати в приватному середовищі розробки / виробництва для розміщення, доступу та публікації своїх приватних пакетів. Хоча це на Windows, але я впевнений, що в Linux є різні альтернативи.

  1. Підмодулі Git - це погана ідея, це дійсно старий спосіб поділитися кодом, який не розбирається, як пакунки, зміна та введення підмодулів - справжня біль.
  2. Папка імпортування джерела - це також погана ідея, знову ж таки, проблема з версією, тому що якщо хтось модифікує залежну папку у залежному сховищі, знову відстежуючи це кошмар.
  3. Будь-які сторонні скриптовані інструменти для імітації розділення пакунків - це марна трата часу, оскільки npm вже надає низку інструментів для управління пакетами.

Ось наш сценарій побудови / розгортання.

  1. Кожен приватний пакет має .npmrcякий міститьregistry=https://private-npm-repository .
  2. Ми публікуємо всі наші приватні пакети в нашому приватному сховищі ProGet.
  3. Кожен приватний пакет містить залежні приватні пакети ProGet.
  4. Наш сервер побудови отримує доступ до ProGet через встановлену нами автентифікацію npm. Ніхто за межами нашої мережі не має доступу до цього сховища.
  5. Наш сервер збирання створює npm-пакет, в bundled dependenciesякому містяться всі пакунки всередині, node_modulesі сервер виробництва ніколи не потребує доступу до NPM або приватних пакетів NPM, оскільки всі необхідні пакети вже в комплекті.

Використання приватного сховища npm має різні переваги,

  1. Не потрібно користувацького сценарію
  2. Вписується в конвеєр, який купує / публікує
  3. Кожен приватний пакет npm міститиме пряме посилання на ваше приватне керування джерелом git, легко налагоджувати та досліджувати помилки в майбутньому
  4. Кожен пакет є знімком, який читається лише зараз, тому щойно опублікований не може бути змінений, і поки ви створюєте нові функції, існуюча база коду зі старшою версією залежних пакетів не впливатиме.
  5. Ви можете легко оприлюднити деякі пакунки та перейти до іншого сховища в майбутньому
  6. Якщо програмне забезпечення вашого приватного постачальника npm змінюється, наприклад, ви вирішили перемістити свій код у приватну хмару реєстру пакетів npm для вузла, вам не потрібно буде вносити жодних змін у свій код.

Це може бути рішенням, але це, на жаль, не для мене. Дякую за ваш час!
MauriceNino

Є також локальне сховище npm, яке встановлюється як невеликий сервер вузлів, verdaccio.org
Kava

-1

Ви шукаєте інструмент npm link. npm linkнадає посилання на локальний пакет npm. Таким чином ви можете зв'язати пакет і використовувати його у своєму головному проекті, не публікуючи його в бібліотеці пакетів npm.

Застосовано до вашої справи використання:

  1. Використовуйте npm linkвсередині вашого sharedпакета. Це встановить місце призначення посилання для майбутніх установок.
  2. Перейдіть до основних проектів. Всередині вашого functionsпакета і використовуйте npm link sharedдля зв’язку спільного пакета та додавання його до node_modulesкаталогу.

Ось посилання на документацію: https://docs.npmjs.com/cli/link.html


Наскільки мені відомо, посилання npm призначене лише для тестування і не працює, якщо ви хочете розгорнути отриманий код (наприклад, мої функції).
MauriceNino

Я бачу, ви, ймовірно, повинні додати цю вимогу до свого питання.
friedow

Він вже згадується у питанні, але я його уточню.
MauriceNino
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.