공통 클래스에 공유 노드 모듈 사용


15

그래서 나는이 구조의 프로젝트를 가지고 있습니다 :

  • 이온 앱
  • 파이어베이스 기능
  • 공유

목표는 shared모듈 에서 공통 인터페이스와 클래스를 정의하는 것입니다 .

제한 사항

로컬로 사용하기 위해 코드를 npm에 업로드하고 싶지 않으며 코드를 전혀 업로드하지 않을 계획입니다. 오프라인에서 100 % 작동해야합니다.

개발 프로세스는 오프라인에서 작동해야하지만 ionic-appfirebase-functions모듈은 firebase (호스팅 및 기능)에 배포됩니다. 따라서 shared모듈 의 코드를 사용할 수 있어야합니다.

내가 지금까지 시도한 것

  • Typescript에서 Project References 를 사용해 보았지만 작동에 가깝지 않았습니다.
  • 이 질문 의 두 번째 답변과 같이 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)

질문

typescripts config 또는 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"
  }
}

현재 soution

공유 모듈에 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속성 predeploypostdeploy원하는 서비스 를 통해 수행됩니다 . 이러한 속성에는 코드를 배포하기 전과 후에 실행할 순차적 명령 배열이 포함되어 있습니다. 또한 이러한 명령은 환경 변수 RESOURCE_DIR( ./functions또는 의 디렉토리 경로 또는 ./ionic-app해당 PROJECT_DIR되는 디렉토리 경로 ) 및 (를 포함하는 디렉토리 경로 )와 함께 호출됩니다 firebase.json.

inside에 대한 predeploy배열을 사용하여 공유 라이브러리의 코드를 Cloud Functions 인스턴스에 배포 된 폴더에 복사 할 수 있습니다. 이렇게함으로써, 하위 폴더에있는 라이브러리 인 것처럼 당신은 단순히 공유 코드를 포함하거나 사용하여 이름을 매핑 할 수 있습니다 타이프 라이터의 경로 매핑 에 (당신이 사용할 수 있도록 명명 된 모듈 ).functionsfirebase.jsontsconfig.jsonimport { hiThere } from 'shared';

predeploy후크 정의 (글로벌 사용은의 설치 shx윈도우 호환성을 위해) :

// 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",
    ...
  }
}

복사 된 라이브러리의 타입 스크립트 소스를 함수 타입 스크립트 컴파일러 구성에 링크 :

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

모듈 이름 "shared"를 복사 된 라이브러리의 패키지 폴더에 연관시킵니다.

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

여러 패키지로 JavaScript (및 TypeScript) 프로젝트를 관리하기위한 도구 인 Lerna 를 사용해보십시오 .

설정

프로젝트의 디렉토리 구조가 다음과 같다고 가정합니다.

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

게시하지 않으려는 모든 모듈과 모듈의 항목에 올바른 액세스 수준 ( privateconfig/access키) 을 지정하십시오 .typingsshared

공유 :

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

이온 앱 :

{
  "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을 사용하여 lerna를 통해 프로젝트 모듈에서 해당 스크립트를 호출하는 npm 스크립트를 정의 할 수도 있습니다 .

{
  "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프로젝트 루트에, NPM은 호출합니다 lerna run compile --streamlerna run compile --stream호출 한 스크립트 호출 compile하여 모듈의 각 package.json파일을.

각 모듈에는 이제 자체 compile스크립트가 있으므로 tsonfig.json모듈 당 파일을 가질 수 있습니다 . 복제가 마음에 들지 않으면 루트 수준 tsconfig 또는 루트 수준 tsconfig와 루트 수준 tsconfig 및 루트 수준에서 상속 된 모듈 수준 tsconfig 파일의 조합으로 벗어날 수 있습니다.

이 설정이 실제 프로젝트에서 어떻게 작동하는지 확인하려면 Serenity / JS 를 살펴 보았습니다.

전개

가진 좋은 점 shared에서 심볼릭 링크 모듈 node_modules에서은 firebase-functionsionic-app, 그리고 devDepedencies아래의 node_modules프로젝트 루트 아래는 (소위 소비자 모듈 어디를 배포해야하는 경우이다 ionic-app예를 들면), 당신이 그와 함께 모든 걸 압축 단지 수 node_modules와하지에 대한 걱정 배포하기 전에 개발 지연을 제거해야합니다.

도움이 되었기를 바랍니다!


모욕! 나는 확실히 그것을 확인하고 이것이 맞는지 볼 것입니다.
MauriceNino

2

git을 사용하여 코드를 관리하는 경우 가능한 또 다른 해결책은을 사용하는 것 git submodule입니다. 사용 git submodule하면 다른 자식 저장소를 프로젝트에 포함시킬 수 있습니다.

사용 사례에 적용 :

  1. 공유 버전 저장소의 현재 버전을 푸시하십시오.
  2. 사용 git submodule add <shared-git-repository-link>주 프로젝트 (들) 내부는 공유 저장소를 연결합니다.

다음은 설명서에 대한 링크입니다. https://git-scm.com/docs/git-submodule


실제로 나쁜 생각은 아니지만 로컬 개발 및 테스트는 기본적으로이 방법으로 진행됩니다.
MauriceNino

0

문제를 올바르게 이해하면 솔루션이 단일 답변보다 더 복잡하며 선호도에 부분적으로 달려 있습니다.

접근법 1 : 로컬 사본

Gulp 를 사용 하여 이미 설명한 작업 솔루션을 자동화 할 수 있지만 IMO는 다른 개발자가 들어 오면 유지 관리가 쉽지 않고 복잡성을 크게 증가시킵니다.

접근법 2 : 모노 레포

세 개의 폴더가 모두 포함 된 단일 리포지토리를 생성하여 단일 프로젝트처럼 동작하도록 연결할 수 있습니다. 위에서 이미 대답했듯이 Lerna 를 사용할 수 있습니다 . 약간의 구성이 필요하지만 일단 완료되면 해당 폴더는 단일 프로젝트로 작동합니다.

접근법 3 : 구성 요소

이러한 각 폴더를 독립형 구성 요소로 취급하십시오. Bit를 살펴보십시오 . 폴더를 더 큰 프로젝트의 작은 부분으로 설정할 수 있으며 해당 구성 요소의 범위 만 개인 계정으로 만들 수 있습니다. 처음 설정하면 별도의 폴더에 업데이트를 적용 할 수도 있으며이를 사용하는 상위 폴더는 자동으로 업데이트를 가져옵니다.

접근법 4 : 패키지

당신은 구체적으로 당신이 npm을 사용하고 싶지 않다고 말했지만, 나는 아래에 설명 된 것처럼 현재 설정 작업을하고 있으며 나를 위해 완벽한 일을하고 있기 때문에 공유하고 싶습니다.

  1. npm또는 yarn각 폴더에 대한 패키지를 작성 하십시오 (두 가지 모두에 대해 범위가 지정된 패키지를 작성할 수 있으므로 코드가 문제가 될 경우에만 코드를 사용할 수 있습니다).
  2. 상위 폴더 (이러한 모든 폴더를 사용)에서 작성된 패키지는 종속성으로 연결됩니다.
  3. webpack을 사용하여 typescript 경로와 함께 webpack 경로 별칭을 사용하여 모든 코드를 번들로 묶습니다.

매력처럼 작동하고 패키지가 로컬 개발을 위해 심볼릭 링크되면 완전히 오프라인으로 작동하며 제 경험상 각 폴더는 개별적으로 확장 가능하고 유지 관리가 매우 쉽습니다.

노트

'자식'패키지는 꽤 큰 크기이므로 이미 사전 컴파일되어 있으며 각 패키지에 대해 별도의 tsconfig를 만들었지 만 아름다운 것은 쉽게 변경할 수 있다는 것입니다. 과거에는 모듈과 컴파일 된 파일의 형식 스크립트와 원시 js 파일을 모두 사용 했으므로 전체가 매우 다양합니다.

도움이 되었기를 바랍니다

***** UPDATE **** 포인트 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 또는 vanilla javascript로 작성된 경우 상위 프로젝트는 신경 쓰지 않습니다 .

일단 설정되면 프로덕션에 완벽하게 작동합니다. 그런 다음 링크하여 오프라인으로 작업하십시오.

  1. firebase-functions프로젝트로 이동하여 씁니다 npm link. 컴퓨터에 로컬로 심볼릭 링크를 만들고 package.json에 설정 한 이름으로 링크를 매핑합니다.
  2. 상위 프로젝트 및 write로 이동 npm link firebase-functions하면 symlink가 생성되고 firebase-functions의 종속성이 생성 한 폴더에 매핑됩니다.

나는 당신이 무언가를 잘못 이해했다고 생각합니다. 나는 npm을 사용하고 싶지 않다고 결코 말하지 않았다. 실제로이 세 가지 모듈 모두 노드 모듈입니다. 방금 모듈을 npm에 업로드하고 싶지 않다고 말했습니다. 그 4 번째 부분을 좀 더 정교하게 설명 할 수 있습니까? 코드 샘플을 제공 할 수 있습니까?
MauriceNino

이 주석으로 길고 읽을 수있는 바와 같이, 내가 다른 대답을 추가합니다
이반 Dzhurov

나의 초기 대답을 업데이트, 그것은 분명 희망
이반 Dzhurov

0

로컬로 사용하기 위해 코드를 npm에 업로드하고 싶지 않으며 코드를 전혀 업로드하지 않을 계획입니다. 오프라인에서 100 % 작동해야합니다.

모든 npm 모듈은 로컬로 설치되며 항상 오프라인으로 작동하지만 사람들이 볼 수 있도록 공개적으로 패키지를 게시하지 않으려는 경우 개인 npm 레지스트리를 설치할 수 있습니다.

ProGet은 개인 개발 / 생산 환경에서 개인 패키지를 호스팅, 액세스 및 게시하기 위해 사용할 수있는 Windows 용 NuGet / Npm 개인 리포지토리 서버입니다. 비록 Windows에 있지만 Linux에서 사용할 수있는 다양한 대안이 있다고 확신합니다.

  1. Git 서브 모듈은 나쁜 생각입니다. 패키지처럼 버전이 지정되지 않은 코드를 공유하는 구식 방법입니다. 서브 모듈을 변경하고 커밋하는 것은 정말 고통 스럽습니다.
  2. 누군가 소스 저장소 폴더가 의존 저장소의 종속 폴더를 수정하면 다시 추적하는 것은 악몽이기 때문에 소스 가져 오기 폴더도 나쁜 생각입니다.
  3. npm은 이미 패키지를 잘 관리 할 수있는 다양한 도구를 제공하므로 패키지 분리를 에뮬레이트하는 타사 스크립트 도구는 시간 낭비입니다.

다음은 빌드 / 배포 시나리오입니다.

  1. 모든 개인 패키지에는가 .npmrc포함되어 있습니다 registry=https://private-npm-repository.
  2. 모든 비공개 패키지를 비공개 호스팅 ProGet 리포지토리에 게시합니다.
  3. 모든 개인 패키지에는 ProGet에 종속 개인 패키지가 포함되어 있습니다.
  4. 우리의 빌드 서버는 우리가 설정 한 npm 인증을 통해 ProGet에 액세스합니다. 네트워크 외부의 어느 누구도이 저장소에 액세스 할 수 없습니다.
  5. 우리의 빌드 서버는 bundled dependencies내부에 모든 패키지를 포함하는 npm 패키지를 생성 node_modules하며 프로덕션 서버는 필요한 모든 패키지가 이미 번들로 제공되므로 NPM 또는 개인 NPM 패키지에 액세스 할 필요가 없습니다.

개인 npm 저장소를 사용하면 여러 가지 장점이 있습니다.

  1. 커스텀 스크립트 불필요
  2. 노드 buid / publish 파이프 라인에 적합
  3. 모든 개인 npm 패키지에는 개인 git 소스 제어에 대한 직접 링크가 포함되어 있으며 향후 오류를 쉽게 디버깅하고 조사 할 수 있습니다.
  4. 모든 패키지는 읽기 전용 스냅 샷이므로 일단 게시 한 후에는 수정할 수 없으며, 새로운 기능을 만드는 동안 이전 버전의 종속 패키지가있는 기존 코드베이스에는 영향을 미치지 않습니다.
  5. 향후 일부 패키지를 쉽게 공개하고 다른 저장소로 옮길 수 있습니다
  6. 예를 들어 코드를 노드의 프라이빗 npm 패키지 레지스트리 클라우드로 이동하기로 결정한 경우와 같이 프라이빗 npm 제공자 소프트웨어가 변경되면 코드를 변경할 필요가 없습니다.

이것은 해결책 일 수도 있지만 불행히도 나에게는 그렇지 않습니다. 그래도 시간 내 주셔서 감사합니다!
MauriceNino

작은 노드 서버로 설치되는 로컬 npm 저장소도 있습니다. verdaccio.org
Akash Kava

-1

찾고있는 도구는 npm link입니다. npm link로컬 npm 패키지에 대한 심볼릭 링크를 제공합니다. 이렇게하면 패키지를 링크하여 npm 패키지 라이브러리에 게시하지 않고 기본 프로젝트에서 사용할 수 있습니다.

사용 사례에 적용 :

  1. 패키지 npm link내부에서 사용하십시오 shared. 향후 설치를 위해 symlink 대상을 설정합니다.
  2. 기본 프로젝트로 이동하십시오. functions패키지 내부 npm link shared에서 공유 패키지를 연결하고 node_modules디렉토리에 추가하는 데 사용합니다 .

다음은 설명서에 대한 링크입니다. https://docs.npmjs.com/cli/link.html


내가 아는 한, npm 링크는 테스트 전용이며 결과 코드 (예 : 내 함수)를 배포하려는 경우 작동하지 않습니다.
MauriceNino

당신은 아마 당신의 질문 에이 요구 사항을 추가해야합니다 참조하십시오.
friedow

이미 질문에 언급되어 있지만 명확하게 설명하겠습니다.
MauriceNino
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.