简体   繁体   中英

Lerna / Nx / Turborepo or other monorepo systems for non-js apps (php)

I have repository with /frontend (JS/Vue) and /backend (PHP) and docker-compose.yml for development. But now I need to add another JS frontend that will be sharing some components, libs etc. I do not want to repeat code, so I found that I can use many tools for this like lerna , nx , turborepo and more for managing monorepo with shared packages.

Problem is that all tutorials and example repos I found are based on 100% JS repos, like React on front + Nest on back etc.

So question - using for ex. nx makes sense on repos with multiple languages, just to leverage it on JS packages? If yes, how directory structure should look like? Or maybe there are better monorepo tools that are designed for multiple languages, not just JS?

I use Angular and PHP (Yii Framework) inside an Nx monorepo and both works fine. No idea how Lerna and Turborepo works, but Nx allow running CLI commands, I use that for the PHP part. My monorepo looks a bit as follow:

- apps // <- folder holding my projects
--- project-1
--- project-2
------ web // <- Main frontend app (Angular/JS)
------ electron // <- other JS apps like mobile, capacitor, extension, ...
------ api // <- PHP api overridden config files

- libs
--- web // <- shared JS stuff, most people call it 'shared' folder instead
------ ui // <- my shared design system
--- electron
--- project-2 // <- specific project overrides and locally shared stuff
------ services
------ data-access 
------ ui
--------- button // <- overriding a component from my design system to only apply in project-2
------ api // <- PHP api but only holding overridden files
--- php
------ api // <- shared PHP api code
------ auth // <- another shared PHP app
------ shell // <- another shared PHP app

- dist // <- is where NX will will put all generated code from all apps/libs 

The main idea is that, every code I write goes inside that libs folder (libs/web for JS and libs/php for PHP) and within a generated Nx library so it has a name (like php-api ) to link it from everywhere and a project.json file holding Nx configs.

That is my base shared code. Written once, for any project or lib needing it.

libs/project-2 will then hold project-2 specific code. Things that are only shared inside that app pages plus things I override from the previously mentioned folders (or previous shared layer) like the button example above.

Code inside apps is as tiny as possible, mainly layouts and configs. That is pretty much how most Nx documentation/tutorials out there would suggest (for JS). I do the same for PHP:

  1. I've put all of my Yii api code inside libs/php/api
  2. Everything specific to the project-2 app (like models, controllers, ...) folders and/or files goes inside libs/project-2/api
  3. main config files (like .env) goes inside apps/project-2/api
  4. Each lib folder needed a project.json file defining it:
{
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "projectType": "library",
  "sourceRoot": "libs/php/api",
  "tags": ["scope:php", "type:api"]
}
  1. In my case, using Angular, there was an angular.js file at root level, where I declared each PHP lib name so Nx detects it, I simply added 2 lines: "php-api": "libs/php/api" and "project-2-api": "libs/project-2/api",

  2. Inside apps/project-2/api folder, I added a project.json file with the following content:

{
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "projectType": "application",
  "sourceRoot": "apps/project-2/api",
  "targets": {
    "build": {
      "executor": "nx:run-commands",
      "options": {
        "commands": [
          "mkdir -p dist/apps/project-2",
          "rsync -rtul libs/php/api dist/apps/project-2",
          "rsync -rtul libs/project-2/api dist/apps/project-2",
          "rsync -rtul apps/project-2/api dist/apps/project-2"
        ],
        "parallel": false
      }
    },
    "serve": {
      "executor": "nx:run-commands",
      "dependsOn": ["build"],
      "options": {
        "commands": ["php -S localhost:8081 -t dist/apps/project-2/api/web"]
      }
    }
  },
  "implicitDependencies": ["php-api"]
}

That defines both build and serve scripts, so I can use Nx cli to run them, the first will just copy PHP files to dist folder (respecting my overriding hierarchy) while the second uses PHP's built-in server to serve the final folder:

> nx run project-2-api:build

> nx run project-2-api:serve

Note that my first script uses rsync -rtul to copy files, you can use cp -R instead, I just found rsync much faster as it has the ability (with those options) to skip unchanged files.

So main idea is that, Most of my code is written once, overrides when needed, a single design system I only override its CSS for a different look with a new app. And I do the same for PHP, even if not directly supported, I simply use nx:run-commands executer for things like moving files, building or deploying live.

Auto update on save

I use VSCode, other IDE may also have a similar way, in my case I use an external extension called run-on-save that executes commands when saving a file based on provided regex. Its configuration for my project looks a bit as follow:

"emeraldwalk.runonsave": {
    "commands": [
      {
        "match": "(/(project-2|php)/(api|yii-shared)/.+).php$",
        "cmd": "nx run project-2-api:build --skip-nx-cache"
      },
      {
        "match": "(/(project-2|php)/(auth|yii-shared)/.+).php$",
        "cmd": "nx run project-2-auth:build --skip-nx-cache"
      },
      {
        "match": "(/(project-2|php)/(shell|yii-shared)/.+).php$",
        "cmd": "nx run project-2-shell:build --skip-nx-cache"
      }
    ]
  }

Every time I hit CTR+S to save a PHP file, dist folder is automatically updated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM