Monorepo 탐색 #3: 소스 빌드

목차


  • Make it build
  • Does it work?
  • The Good

  • The Bad
  • What can we solve with scripting?


  • 오늘 우리는 확실히 작동할 무언가를 시도할 것입니다. 그것은 우리가 끝내게 될 개발자 경험이 얼마나 나쁜지에 대한 질문일 뿐입니다. 우리는 Typescript 라이브러리를 Javascript로 컴파일할 것입니다.

    이것은 Typescript를 종속성 방정식에서 완전히 제거하기 때문에 작동하지만 이제 소비자가 볼 수 있기 전에 소스 코드에 대한 변경 사항을 컴파일해야 하기 때문에 워크플로에 큰 쐐기를 박을 것입니다. 그리고 나는 우리가 다른 단점도 찾을 것이라고 생각합니다.

    일부 도구 또는 스크립팅으로 이러한 문제를 수정하거나 완화할 수 있기를 바랍니다. 하지만 이 기사는 이 기사 시리즈를 끝낼 황금 솔루션을 밝히기 위한 것이 아닙니다… 작동하는 것으로 오늘을 끝내기 위해. 때때로 우리는 문제를 더 잘 이해하기 위해 도약해야 합니다.

    구축

    To figure out what it means to build Typescript to Javascript let's first try it out on the libs/types package. It's quite straightforward to set up compiling:
    1) Ensure tsconfig.json has the outDir field specified, that's the folder Javascript gets output to:

    $ cd libs/types
    $ cat tsconfig.json
      "compilerOptions": {
        "outDir": "./dist"
    

    2) Add a build script:

    $ cat package.json
      "scripts": {
        "build": "tsc -b"
    $ pnpm build
    $ tree dist 
    dist
    ├── index.d.ts
    ├── index.js
    └── index.js.map
    

    3) Ensure package.json entry-point fields are set to point to the files in the dist folder:

    $ cat package.json 
      "main": "dist/index.js",
      "types": "dist/index.d.ts",
    

    With that in place this library can now be used as a normal Javascript dependency, and consumers don't have to know it's written in Typescript. Next we just have to apply this to all the code!

    작동합니까?

    So, the result is the usual good overview:

    webby
    ├── apps
    │  ├── api
    │  │  ├── package.json
    │  │  ├── prisma/
    │  │  ├── src/
    │  │  └── tsconfig.json
    │  └── web
    │     ├── package.json
    │     ├── src/
    │     ├── tsconfig.json
    │     └── typings/
    ├── libs
    │  ├── analytics
    │  │  ├── jest.config.js
    │  │  ├── package.json
    │  │  ├── src/
    │  │  └── tsconfig.json
    │  ├── logging
    │  │  ├── package.json
    │  │  ├── src/
    │  │  └── tsconfig.json
    │  └── types
    │     ├── package.json
    │     ├── src/
    │     └── tsconfig.json
    ├── package.json
    └── tsconfig.json
    

    ℹ️ This project is prepared on GitHub1s if you'd like to familiarize yourself with the code.



    작업하기 쉬운 도구였기 때문에 종속성을 설치하고 모든 패키지에서 build 스크립트를 매우 쉽게 실행할 수 있습니다.

    $ cd ../..
    $ pnpm install
    Scope: all 6 workspace projects
    $ pnpm -r run build
    Scope: all 6 workspace projects
    


    그래서 web 지금 작동합니까?

    $ cd apps/web
    $ pnpm start
    [razzle] > Started on port 3000
    


    좋다 (좋아요. 그리고 api ?

    $ cd ../api
    $ pnpm start
    [api] > prisma generate && nodemon -w src/* -x 'ts-node src/api.ts'
    [api] Error: Command failed with exit code 1: npm install -D [email protected]
    [api]  ERROR  Command failed with exit code 1.
    


    안 돼! 하지만 잠깐, 우리가 pnpm을 사용할 때 왜 Command failed: npm install라고 말합니까?

    이것은 Prisma are working on 의 알려진 문제인 것으로 밝혀졌습니다. 현재 해결 방법은 특정 버전을 설치하는 것입니다(이 글을 작성할 때 버전2.27.0-integration-fix-sdk-pnpm.2 사용을 권장함).
    package.json 의 종속성을 변경하면 api 작동합니까?

    $ pnpm install
    - @prisma/client 2.26.0
    + @prisma/client 2.27.0-integration-fix-sdk-pnpm.2
    - prisma 2.26.0
    + prisma 2.27.0-integration-fix-sdk-pnpm.2
    $ pnpm start
    [api] api started at http://localhost:3002
    


    세상에, 만세! 🎉

    모든 것을 합치면 이제 루트에서 완전히 제품을 올릴 수 있습니다.

    $ cd ../..
    $ git clean -dxi .; # this prompts you for what to delete so it's safe to run
    $ pnpm install && pnpm build
    $ pnpm start
    apps/web start: [razzle] > Started on port 3000
    apps/api start: [api] api started at http://localhost:3002
    


    우리는 해냈다!

    좋은

    Taking a step back there are some things I very much like about this pattern:

    • By building the code we are no longer bound to writing it in Typescript. Any language that compiles to Javascript will do. So encapsulation of each project has increased, which I'll count as wonderful.

    • This allows us a lot of flexibility in what a library produces: For this article-series the libraries are just groupings of code, their built code is identical in function to the source. But what if we imagine we wanted to generate something different than the source-code? What if we had a library whose source-code downloads Swagger documentation from some remote API and generate a Javascript client? To do that we must have a build step, and with this article's approach building is now a "first-class concept" so we don't have to make weird one-off exceptions to support something like that.

    • I really appreciate the simplicity of boiling everything down to Javascript, there's just that much less chance of anything going wrong.

    Do you see other good things about this pattern? I'd love to hear your take on this.

    But there are some big drawbacks too! 😓

    나쁜

    • We now have a workflow where changes to a library aren't reflected in consumers until the library gets rebuilt. So we have to remember to run pnpm build after every change 😬. That's not good because it's so easy to forget, and then whatever work we've just done will seem like it's missing in the consumer in possibly subtle and confusing ways. I don't know you so maybe you wouldn't have a problem with this, but I think for newcomers and juniors it'll be that little extra annoyance that we are so desperately trying to avoid.

    • We end up with boilerplate code and configurations that are identical across all projects, e.g. tsconfig.json must specify outDir and package.json must have a build script + specify main & types fields… it's just an annoying amount of small details that have to be exactly right and it gets worse the more projects we add.

    Are there other downsides you can think of? I'd love to hear them!

    스크립팅으로 무엇을 해결할 수 있습니까?

    We first and foremost need to not manually rebuild all the time. I see two paths ahead:

    1. Dependency rebuilds are invoked whenever consumers run their scripts. So every time apps/web runs start it would first go out and rebuild its dependencies.
    2. Rebuild dependencies via a watcher, so every time a package's code changes it rebuilds itself.

    Can you think of other proposals?

    We also would benefit from some solution to the boilerplate code and configurations, e.g. if a script could check all packages and fix or warn about misconfigurations then we'd probably have alleviated the issue well enough.

    This isn't the article where we write the scripts or even decide exactly how to do it, but maybe that's a topic for the next article? At this point I'd very much like to hear from you, so please leave a comment with your thoughts or suggestions.

    좋은 웹페이지 즐겨찾기