【问题标题】:How to create a mono-repo project structure with angular-cli (6.x)如何使用 angular-cli (6.x) 创建单一仓库项目结构
【发布时间】:2025-11-23 17:25:01
【问题描述】:

我想使用 angular-cli 生成一个新的工作区

目前唯一的方法是运行:

ng new asdf
cd asdf
ng g application whatever

但是所有src/ 文件仍然存在(以及现在不一致存储的项目的原始 angular.json 信息)。如果没有src 文件夹,就无法创建新的存储库...当我使用自定义原理图执行此操作时,基本上复制了 angular-cli 的 ng-new 并删除了 https://github.com/angular/angular-cli/blob/6449a753641340d8fc19a752e1a1ced75f974efa/packages/schematics/angular/ng-new/index.ts#L61 schematic('application', applicationOptions),

每当我运行它...

$ ng new asdf -c=my-schematic
$ cd asdf
$ ng g application whatever
    {"inlineStyle":false,"inlineTemplate":false,"routing":false,"prefix":"app","style":"css","skipTests":false,"skipPackageJson":false,"name":"whatever"}
CREATE projects/whatever-e2e/protractor.conf.js (752 bytes)
CREATE projects/whatever-e2e/src/app.e2e-spec.ts (304 bytes)
CREATE projects/whatever-e2e/src/app.po.ts (208 bytes)
CREATE projects/whatever-e2e/tsconfig.e2e.json (219 bytes)

只有 e2e 文件被放入projects/angular.json 文件大部分保持为空:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {},
  "cli": {
    "defaultCollection": "my-schematic"
  }
}

我怀疑这与应用程序示意图中出现静默失败或路径关闭有关...但我想先检查是否有更简单/更首选的方法。

$ ng --version                                                 

Angular CLI: 6.1.0-rc.0                                        
Node: 10.1.0                                                   
OS: win32 x64                                                  
Angular: 6.0.7                                                 
... animations, common, compiler, compiler-cli, core, forms    
... http, language-service, platform-browser                   
... platform-browser-dynamic, router                           

Package                      Version                           
------------------------------------------------------         
@angular-devkit/architect    0.7.0-rc.0                        
@angular-devkit/core         0.6.8                             
@angular-devkit/schematics   0.6.8                             
@angular/cdk                 6.3.1                             
@angular/cli                 6.1.0-rc.0                        
@angular/material            6.3.1                             
@schematics/angular          0.6.8                             
@schematics/update           0.7.0-rc.0                        
rxjs                         6.2.1                             
typescript                   2.7.2     

编辑注意我熟悉 3rd-party 库(例如 Nrwl/Nx),但我试图避免这些。我也把这个问题发到了 GitHub:https://github.com/angular/angular-cli/issues/11402

【问题讨论】:

  • 我看到你说,“但是所有的 src/ 文件仍然存在(以及现在不一致存储的项目的原始 angular.json 信息)。没有没有办法创建新的 repo src 文件夹。”您有一个*工作区,其中包含一堆子应用程序。可以提交*工作区,并且每个应用程序都作为子模块提交(例如在 git 中)。每个都有自己的开发流程等。顶层可以用作子模块的组合机制(同样,每个模块都有不同的流程),所有这些都在一个父仓库中。那不行吗?
  • 我正在尝试使用 angular-cli 来完成这一切。如果我使用 ng new asdf 然后添加一些(比如 5 个)“应用程序”(使用 ng g app)...你将在 asdf/src/ 内有一个应用程序,在 asdf/projects/ 内有 5 个应用程序都共享一个包.json / angular.json 文件。甚至 e2e 测试也在不同的位置(asdf/e2e/ 中的一个和asdf/projects/ 中的 5 个)。这就是我要解决的不一致问题。我宁愿ng new 有一个--blank 选项(或其他东西),然后就可以在项目中拥有所有东西/而不必手动移动东西。
  • 嗯...我不确定我是否认为作为单一回购协议的不一致,每个模块都有自己的 e2e 测试等似乎与这个想法一致,等等。我有点挣扎的一件事是 package.json 的事情。假设我想在其中一个子应用程序中使用 ngrx。我必须在* package.json 中进行安装等。由于我无法为每个应用程序指定不同的 package.jsons,所以 ng g 应用程序最终看起来像是一个美化的功能模块。当然,我可以创建不同的“舞台”子应用程序作为特定的合成器,但是那里的流程开始变得模糊。
  • 从组织/文件夹结构的角度来看是不一致的。就我个人而言,我不使用 git 子模块(看不到重点),因此不需要单独的 package.json 文件(尤其是摇树等)。它也不会真正影响流程,因为合并冲突等是由修改重叠文件引起的。仍然可以对 repo 中的特定应用程序进行拉取请求,并且一个“领导”可以为一个团队审查所有这些请求。
  • 我也相信这是不一致的行为,我的意思是我们公司开发的大多数项目都有客户端和管理端。目前,客户端位于 projectname -> src 下,而管理端位于 project-name->projects->app-name->src 下,我认为这不是一个好的结构。顺便说一句,我们仍然可以在“项目”中创建客户端目录并将初始 src 文件夹移动到那里,但是我们必须基于此更改所有它的初始路径,所以如果我们必须手动执行此操作。

标签: angular angular-cli angular-schematics


【解决方案1】:

目前没有办法按照我的意愿进行操作。

hacky 的解决方法是运行:

ng new <mono-repo name>

cd 进去 (cd <mono-repo name>)

生成第一个应用名称 (ng generate application <app-name>)

然后删除原来的src/e2e文件夹(rm -rf src e2e)和angular.json中对应的条目。

从那时起,无论何时生成库/应用程序,所有内容都将位于项目文件夹中。

【讨论】:

  • 因为 Angular 7 --createApplication=false 应该可以解决问题
【解决方案2】:

如果您不希望您的初始应用程序驻留在 /src 中,您可以按照以下两个步骤操作:

ng new my-workspace --create-application=false --defaults
  • --create-application 告诉 Angular CLI 不要生成初始应用程序。
  • --defaults 告诉 Angular CLI 不要提示您有关路由和 CSS 预处理器的信息
ng generate application my-app

通过这两个步骤,您最终将得到如下文件结构:

.
├── .git/
├── node_modules/
├── projects/
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── package.json
├── tsconfig.json
├── tslint.json
└── yarn.lock

您会在projects文件夹中找到您的应用程序及其对应的e2e应用程序:

projects/
├── my-app/
└── my-app-e2e/

【讨论】:

    【解决方案3】:

    改用ng g application {project-name}

    结果

    还有Angular.json

    {
      "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
      "version": 1,
      "newProjectRoot": "projects",
      "projects": {
        "mono-repo": {
          "root": "",
          "sourceRoot": "src",
          "projectType": "application",
          "prefix": "app",
          "schematics": {},
          "architect": {
            "build": {
              "builder": "@angular-devkit/build-angular:browser",
              "options": {
                "outputPath": "dist/mono-repo",
                "index": "src/index.html",
                "main": "src/main.ts",
                "polyfills": "src/polyfills.ts",
                "tsConfig": "src/tsconfig.app.json",
                "assets": [
                  "src/favicon.ico",
                  "src/assets"
                ],
                "styles": [
                  "src/styles.css"
                ],
                "scripts": []
              },
              "configurations": {
                "production": {
                  "fileReplacements": [
                    {
                      "replace": "src/environments/environment.ts",
                      "with": "src/environments/environment.prod.ts"
                    }
                  ],
                  "optimization": true,
                  "outputHashing": "all",
                  "sourceMap": false,
                  "extractCss": true,
                  "namedChunks": false,
                  "aot": true,
                  "extractLicenses": true,
                  "vendorChunk": false,
                  "buildOptimizer": true
                }
              }
            },
            "serve": {
              "builder": "@angular-devkit/build-angular:dev-server",
              "options": {
                "browserTarget": "mono-repo:build"
              },
              "configurations": {
                "production": {
                  "browserTarget": "mono-repo:build:production"
                }
              }
            },
            "extract-i18n": {
              "builder": "@angular-devkit/build-angular:extract-i18n",
              "options": {
                "browserTarget": "mono-repo:build"
              }
            },
            "test": {
              "builder": "@angular-devkit/build-angular:karma",
              "options": {
                "main": "src/test.ts",
                "polyfills": "src/polyfills.ts",
                "tsConfig": "src/tsconfig.spec.json",
                "karmaConfig": "src/karma.conf.js",
                "styles": [
                  "styles.css"
                ],
                "scripts": [],
                "assets": [
                  "src/favicon.ico",
                  "src/assets"
                ]
              }
            },
            "lint": {
              "builder": "@angular-devkit/build-angular:tslint",
              "options": {
                "tsConfig": [
                  "src/tsconfig.app.json",
                  "src/tsconfig.spec.json"
                ],
                "exclude": [
                  "**/node_modules/**"
                ]
              }
            }
          }
        },
        "mono-repo-e2e": {
          "root": "e2e/",
          "projectType": "application",
          "architect": {
            "e2e": {
              "builder": "@angular-devkit/build-angular:protractor",
              "options": {
                "protractorConfig": "e2e/protractor.conf.js",
                "devServerTarget": "mono-repo:serve"
              }
            },
            "lint": {
              "builder": "@angular-devkit/build-angular:tslint",
              "options": {
                "tsConfig": "e2e/tsconfig.e2e.json",
                "exclude": [
                  "**/node_modules/**"
                ]
              }
            }
          }
        },
        "project-alpha": {
          "root": "projects/project-alpha/",
          "sourceRoot": "projects/project-alpha/src",
          "projectType": "application",
          "prefix": "app",
          "schematics": {},
          "architect": {
            "build": {
              "builder": "@angular-devkit/build-angular:browser",
              "options": {
                "outputPath": "dist/project-alpha",
                "index": "projects/project-alpha/src/index.html",
                "main": "projects/project-alpha/src/main.ts",
                "polyfills": "projects/project-alpha/src/polyfills.ts",
                "tsConfig": "projects/project-alpha/tsconfig.app.json",
                "assets": [
                  "projects/project-alpha/src/favicon.ico",
                  "projects/project-alpha/src/assets"
                ],
                "styles": [
                  "projects/project-alpha/src/styles.css"
                ],
                "scripts": []
              },
              "configurations": {
                "production": {
                  "fileReplacements": [
                    {
                      "replace": "projects/project-alpha/src/environments/environment.ts",
                      "with": "projects/project-alpha/src/environments/environment.prod.ts"
                    }
                  ],
                  "optimization": true,
                  "outputHashing": "all",
                  "sourceMap": false,
                  "extractCss": true,
                  "namedChunks": false,
                  "aot": true,
                  "extractLicenses": true,
                  "vendorChunk": false,
                  "buildOptimizer": true
                }
              }
            },
            "serve": {
              "builder": "@angular-devkit/build-angular:dev-server",
              "options": {
                "browserTarget": "project-alpha:build"
              },
              "configurations": {
                "production": {
                  "browserTarget": "project-alpha:build:production"
                }
              }
            },
            "extract-i18n": {
              "builder": "@angular-devkit/build-angular:extract-i18n",
              "options": {
                "browserTarget": "project-alpha:build"
              }
            },
            "test": {
              "builder": "@angular-devkit/build-angular:karma",
              "options": {
                "main": "projects/project-alpha/src/test.ts",
                "polyfills": "projects/project-alpha/src/polyfills.ts",
                "tsConfig": "projects/project-alpha/tsconfig.spec.json",
                "karmaConfig": "projects/project-alpha/karma.conf.js",
                "styles": [
                  "projects/project-alpha/src/styles.css"
                ],
                "scripts": [],
                "assets": [
                  "projects/project-alpha/src/favicon.ico",
                  "projects/project-alpha/src/assets"
                ]
              }
            },
            "lint": {
              "builder": "@angular-devkit/build-angular:tslint",
              "options": {
                "tsConfig": [
                  "projects/project-alpha/tsconfig.app.json",
                  "projects/project-alpha/tsconfig.spec.json"
                ],
                "exclude": [
                  "**/node_modules/**"
                ]
              }
            }
          }
        },
        "project-alpha-e2e": {
          "root": "projects/project-alpha-e2e/",
          "projectType": "application",
          "architect": {
            "e2e": {
              "builder": "@angular-devkit/build-angular:protractor",
              "options": {
                "protractorConfig": "projects/project-alpha-e2e/protractor.conf.js",
                "devServerTarget": "project-alpha:serve"
              },
              "configurations": {
                "production": {
                  "devServerTarget": "project-alpha:serve:production"
                }
              }
            },
            "lint": {
              "builder": "@angular-devkit/build-angular:tslint",
              "options": {
                "tsConfig": "projects/project-alpha-e2e/tsconfig.e2e.json",
                "exclude": [
                  "**/node_modules/**"
                ]
              }
            }
          }
        }
      },
      "defaultProject": "mono-repo"
    }

    【讨论】:

    • ng g appng g application 是同一件事,您必须在已经建立的项目中进行(如上所述,该项目具有 src 文件夹)。这不能回答我的问题
    • 显然我已经习惯使用将app 别名为application 的示意图。我已经编辑了我的问题以澄清,但这是我的错字/没有影响我的意图
    • 我原本也有同样的想法,但重读并看到他已经知道这一点,正在寻找其他东西。我认为 g app 基本上创建了一个美化的功能模块;是的,它可以独立运行和测试......有点。它仍然依赖于工作区 package.json。如果您以 npm mod 或类似的形式发布(它会在 deps 中拉出),这很好,但并不是真正的单一回购必杀技。您的项目“应用程序”仍然需要由包含 package.json 的工作区组成。在最近的一份合同中,我自己一直在做这件事。有趣的问题要解决。
    【解决方案4】:

    我在这里是如何使用 Nx 和 Angular CLI 执行此操作的指南 here 最简单的方法是从生成一个空的 Angular CLI 项目开始:

    ng new --createApplication false
    

    或者如果您使用 NX:

    create-nx-workspace myworkspacedemo
    

    从这里您可以在 app 文件夹中生成应用程序:

    ng g app myapp
    

    并在 libs 文件夹中生成库:

    ng g lib mylib
    

    如果您使用的是 NX 原理图,这还会提示您进行许多额外的配置选项,例如生成延迟加载的模块、路由、使用 Jest 等...

    【讨论】:

      【解决方案5】:

      好吧,创建 monorepo 架构太容易了
      让我们看看

      通过这样做,您指定了一个不包含任何架构的架构 应用。这给了你这样的代码......

      哦,看来您已经创建了架构
      现在进入您的代码编辑器并创建您的应用程序

      现在是时候检查一切是否正常。
      让我们使用您想要的任何随机端口运行...

      您可以在单一架构中拥有多个应用程序,它们都将使用相同的节点模块
      因此,将来一旦您更新了任何模块,您的所有应用程序都将访问更新后的应用程序,因此无需逐个更新它们

      除此之外,您还可以在不同的端口上运行所有应用程序并单独创建构建

      【讨论】:

        最近更新 更多