Monorepo 탐색 #1: 프로젝트 폴더를 만들 수 없나요?

목차


  • The Good

  • The Bad
  • Why don't we script that?
  • What about Yarn?


  • 먼저 간단한 솔루션을 제거해 보겠습니다. 동일한 저장소 내의 다른 프로젝트로 모든 것을 이동할 수 없습니까?

    이를 테스트하기 위해 webapi를 두 개의 개별 앱으로 추출하고 공유 종속성을 위한 libs 폴더를 만듭니다. 파일을 이리저리 옮기면 다음과 같은 구조가 됩니다.

    webby
    ├── apps
    │  ├── api/
    │  └── web/
    ├── libs
    │  ├── analytics/
    │  ├── logging/
    │  └── types/
    └── tsconfig-base.json
    

    webpackage.json 를 보면 web 에서 완전히 사용되는 작은 종속성 목록을 볼 수 있습니다.

        "express": "^4.17.1",
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "react-router-dom": "^5.2.0",
        "types": "file:../../libs/types"
    


    ℹ️ As before I've prepared this project on GitHub1s if you'd like to familiarize yourself with the code. But note that now each app and lib are their own projects: You can open them up individually to avoid "drowning" in all the other code (but to do that you'll have to clone the repository and check out branch attempt-can't-we-just because GitHub1s doesn't support opening subfolders).




    좋은

    The overview has improved greatly! The high-level architecture is now easily readable: We have two apps, and some libraries, so as a new hire I can quickly get a feel for what large-scale projects we work on.

    And if we dive into web we see its package.json references the local dependency ../../libs/types , which makes it simple to understand at-a-glance that if I work on web I only need to understand libs/types code to get my work done. How amazing!

    It is worth acknowledging though that, yes, there are now more files. Where analytics was one file before, it is now a whole project which means it has its own package.json , tsconfig.json , + other scaffolding files. This looks quite bad with our example because our libraries are so anemic, but keep in mind we're pretending our extracted projects are those we agree are complex enough to warrant extraction. If each project actually had dozens of files and a non-trivial amount of dependencies then the high-level clarity would outweigh the added number of files. But it is a tradeoff: Clarity in the overview causes more project-bootstrapping files to appear. It's simpler, not necessarily easier , 그리고 당신만이 당신의 균형을 결정할 수 있습니다.

    나쁜

    Unfortunately this solution doesn't work 😱😅. It's a frustrating conclusion because it can appear to work, but actually it breaks in various subtle ways

    ℹ️ BTW, throughout this if you'd like to get back to a clean checkout state you can run this command: git clean -dxi .

    If we begin from a clean checkout and start the web app we immediately hit an error:

    $ cd apps/web
    $ npm ci
    $ npm start
    ../../libs/types/src/index.ts(1,23): error TS2307: Cannot find module 'type-fest' or its corresponding type declarations.
    

    What happened? This has to do with how npm installs local dependencies:

    When we run npm ci (or npm install , it's the same problem either way) local dependencies are handled in a special way: A local dependency is symlinked into the node_modules folder. In this case web depends on libs/types and we can see how it's just a symlink by looking in web's node_modules folder:

    $ ls -a node_modules | grep types
    types -> ../../../libs/types
    

    But it is just a symlink, npm didn't install the dependencies of libs/types for us like it does for normal dependencies, and so we get the Cannot find module 'type-fest' error because the dependency tree of libs/types hasn't been resolved.

    Does that mean if we manually install dependencies for libs/types then web will start working?

    $ cd ../../libs/types/
    $ npm ci
    $ cd ../../apps/web
    $ npm start
    > Started on port 3000
    

    Voila! But hang on, this is a brittle and time-wasting workflow because we have to manually install each of our own dependencies… that's what npm is supposed to do for us!

    왜 스크립트를 작성하지 않습니까?



    어쩌면 우리가 이 문제를 해결할 수 있을까요? 모든 종속성을 한 번에 설치하는 빠른 방법은 다음과 같습니다.

    ℹ️ BTW I'm on macOS so I'll use its built-in tooling just to keep things simple, but of course a more mature, cross-platform solution can be made if needed.



    $ cd ../..
    $ for p in ./*/*; do; (cd "${p}" && npm ci > /dev/null && echo "Installed ${p}"); done
    Installed ./apps/api
    Installed ./apps/web
    Installed ./libs/analytics
    Installed ./libs/logging
    Installed ./libs/types
    


    이제 모든 것이 작동합니다.

    음, 그렇지 않습니다. web는 작동하지만 api는 작동하지 않습니다.

    $ cd apps/api
    $ npm start
    ../../libs/analytics/src/index.ts(8,3): error TS2564: Property 'uninitializedProperty' has no initializer and is not definitely assigned in the constructor.
    


    오 소년… 지금 무슨 일이야?

    글쎄, 이것은 내가 본 실제 시나리오를 모방하기 위해 의도적으로 넣은 경우입니다. libs/analytics는 유효한 엄격한 Typescript가 아니며 Typescript 설정 strict:false에서만 작동합니다. 자체 프로젝트로 괜찮습니다. libs/analytics 의 test-suite를 실행하여 시연할 수 있습니다.

    $ cd ../../libs/analytics
    $ npm test
    Ran all test suites.
    


    그리고 tsconfig.json 파일을 보면 strict:false 옵션이 올바르게 지정되어 있음을 알 수 있습니다.

    $ cat tsconfig.json
      "compilerOptions": {
        "strict": false
      },
    


    그러나 apps/api는 strict와 함께 작동하므로 자체적으로 strict:true를 지정하지만 실행될 때 api의 TypeScript 구성을 통해 분석 코드를 가져옵니다.

    이 문제를 해결하는 방법을 잘 모르겠습니다. 이것이 Typescript 참조의 의미입니까? 각 하위 프로젝트를 빌드하고 빌드 출력만 사용해야 합니까? 여러분의 아이디어와 제안을 댓글로 남겨주세요!

    얀은 어떻습니까?

    Maybe it's just npm that's the problem? Let's give Yarn a try.

    First let's reset the repo and install yarn:

    $ cd ../..
    $ git clean -dxi .
    $ npm install --global yarn
    

    And now we can start web :

    $ cd apps/web
    $ yarn install
    $ yarn start
    > Started on port 3000
    

    Hey that worked! Yarn actually fully installs local dependencies, including resolving their transient dependencies. So it avoids the "type-test" error 🎉

    But this has a problem too: The dependency isn't "live", meaning changes to libs/types aren't reflected in apps/web until it re-installs its dependencies. That's not a good workflow!, we want to just change code and have it all working together, not worry about what state each project's node_modules folders is in.

    And besides, apps/api has a problem too:

    $ cd ../api
    $ yarn install
    $ yarn start
    SyntaxError: Cannot use import statement outside a module
    

    Is there a solution for this? Some Yarn or Typescript setting to use that will make it all work?


    It feels a lot like we're chasing down problems that I've created for myself. We can't be the first ones to tackle this problem, right? I hope I've just missed a chunk of documentation that will set us right, if you got any suggestions I'm all ears!

    좋은 웹페이지 즐겨찾기