Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Set up Docker Compose
uses: docker/setup-compose-action@v1
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ The projects that can be added to a Monux monorepo also provide a lot of functio
- [Angular website](#angular-website)
- [Angular library](#angular-library)
- [LoopBack](#loopback)
- [Nest](#nest)
- [Typescript library](#typescript-library)
- [Wordpress](#wordpress)
- [Add projects manually](#add-projects-manually)
Expand Down Expand Up @@ -301,13 +302,14 @@ They will give you a nice overview of all monorepos on your machine and their re
## Angular website
## Angular library
## LoopBack
## Nest
## Typescript library
## Wordpress
## Add projects manually
One of the reasons that Monux was created was the amount of abstraction layers added by tools like Nx.<br>
The magic was really cool as long as everything worked, but made debugging issues much harder.

For that reason, Monux is simply using npm workspaces under the hood and none of its magic is hidden from the developer, besides maybe validating and creating environment files, which is explained in detail in [Handling environment variables](#handling-environment-variables).
For that reason, Monux is simply using npm workspaces under the hood and none of its magic is hidden from the developer. Besides maybe validating and creating environment files, which is explained in detail in [Handling environment variables](#handling-environment-variables).

Because of that it should be fairly simple to add any project/framework/library manually by just taking a look at existing projects.

Expand Down
1 change: 1 addition & 0 deletions jest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const config = {
modulePathIgnorePatterns: ['tmp'],
// coverage
collectCoverage: true,
coverageProvider: 'v8',
coverageDirectory: '<rootDir>/__testing__/coverage',
coveragePathIgnorePatterns: [
'/node_modules/',
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "monux-cli",
"version": "2.2.2",
"version": "2.2.3",
"license": "MIT",
"main": "index.js",
"engines": {
Expand Down Expand Up @@ -33,10 +33,10 @@
"start": "npm run build && cd sandbox && node ../dist/index.js",
"build": "tsc",
"clear": "rm -rf sandbox && mkdir sandbox && npm run start i",
"test": "node --max_old_space_size=4096 ./node_modules/jest/bin/jest.js",
"test": "jest --runInBand",
"lint": "eslint . --max-warnings=0",
"lint:fix": "eslint . --max-warnings=0 --fix",
"prepublishOnly": "npm i && npm run lint && npm run build"
"prepublishOnly": "npm i && npm run build"
},
"dependencies": {
"chalk": "^4.1.2",
Expand Down
48 changes: 16 additions & 32 deletions src/__testing__/mock/constants.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,53 @@
import { ANGULAR_JSON_FILE_NAME, ANGULAR_ROUTES_FILE_NAME, APP_CONFIG_FILE_NAME, APPS_DIRECTORY_NAME, DEV_DOCKER_COMPOSE_FILE_NAME, PROD_DOCKER_COMPOSE_FILE_NAME, ENV_FILE_NAME, ENVIRONMENT_MODEL_TS_FILE_NAME, ENVIRONMENT_TS_FILE_NAME, ESLINT_CONFIG_FILE_NAME, GLOBAL_ENVIRONMENT_MODEL_FILE_NAME, LIBS_DIRECTORY_NAME, PACKAGE_JSON_FILE_NAME, LOCAL_DOCKER_COMPOSE_FILE_NAME } from '../../constants';
/* eslint-disable jsdoc/require-jsdoc */
import { ANGULAR_JSON_FILE_NAME, ANGULAR_ROUTES_FILE_NAME, APP_CONFIG_FILE_NAME, APPS_DIRECTORY_NAME, DEV_DOCKER_COMPOSE_FILE_NAME, PROD_DOCKER_COMPOSE_FILE_NAME, ENV_FILE_NAME, ENVIRONMENT_MODEL_TS_FILE_NAME, ENVIRONMENT_TS_FILE_NAME, ESLINT_CONFIG_FILE_NAME, GLOBAL_ENVIRONMENT_MODEL_FILE_NAME, LIBS_DIRECTORY_NAME, PACKAGE_JSON_FILE_NAME, LOCAL_DOCKER_COMPOSE_FILE_NAME, WORKSPACE_FILE_NAME, BASE_TS_CONFIG_FILE_NAME } from '../../constants';
import { OmitStrict } from '../../types';
import { getPath, Path } from '../../utilities';

// eslint-disable-next-line jsdoc/require-jsdoc
export const MAX_ADD_TIME: number = 60000;

export const MAX_GEN_CODE_TIME: number = 10000;

export const MAX_FAST_TIME: number = 300;

export const MAX_INSTANT_TIME: number = 100;

export type MockConstants = {
// eslint-disable-next-line jsdoc/require-jsdoc
readonly PROJECT_DIR: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly APPS_DIR: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly LIBS_DIR: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly DOCKER_COMPOSE_YAML: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly DEV_DOCKER_COMPOSE_YAML: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly LOCAL_DOCKER_COMPOSE_YAML: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_ESLINT_CONFIG_MJS: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_PACKAGE_JSON: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_APP_NAME: string,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_APP_DIR: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_APP_COMPONENT_TS: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_APP_COMPONENT_HTML: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_APP_ROUTES_TS: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_ROUTES_TS: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_APP_CONFIG_TS: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_JSON: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_ENVIRONMENT_MODEL: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ANGULAR_ENVIRONMENT: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly TS_LIBRARY_DIR: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly TS_LIBRARY_SCOPE: string,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly TS_LIBRARY_PACKAGE_JSON: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ROOT_PACKAGE_JSON: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly TS_LIBRARY_NAME: string,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly ENV: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly GLOBAL_ENV_MODEL: Path,
// eslint-disable-next-line jsdoc/require-jsdoc
readonly GITHUB_WORKFLOW_DIR: Path
readonly GITHUB_WORKFLOW_DIR: Path,
readonly WORKSPACE_JSON: Path,
readonly BASE_TS_CONFIG_JSON: Path
};

// eslint-disable-next-line jsdoc/require-jsdoc
export type FileMockConstants = OmitStrict<
MockConstants,
'ANGULAR_APP_DIR' | 'APPS_DIR' | 'LIBS_DIR' | 'PROJECT_DIR' | 'TS_LIBRARY_DIR' | 'GITHUB_WORKFLOW_DIR'
| 'ANGULAR_APP_NAME' | 'TS_LIBRARY_SCOPE' | 'TS_LIBRARY_NAME'
>;

// eslint-disable-next-line jsdoc/require-jsdoc
export type DirMockConstants = OmitStrict<
MockConstants,
keyof FileMockConstants | 'TS_LIBRARY_NAME' | 'ANGULAR_APP_NAME' | 'TS_LIBRARY_SCOPE'
Expand Down Expand Up @@ -114,7 +96,9 @@ export function getMockConstants(projectName: string): MockConstants {
ROOT_PACKAGE_JSON: getPath(PROJECT_DIR, PACKAGE_JSON_FILE_NAME),
ENV: getPath(PROJECT_DIR, ENV_FILE_NAME),
GLOBAL_ENV_MODEL: getPath(PROJECT_DIR, GLOBAL_ENVIRONMENT_MODEL_FILE_NAME),
GITHUB_WORKFLOW_DIR: getPath(PROJECT_DIR, '.github', 'workflows')
GITHUB_WORKFLOW_DIR: getPath(PROJECT_DIR, '.github', 'workflows'),
WORKSPACE_JSON: getPath(PROJECT_DIR, WORKSPACE_FILE_NAME),
BASE_TS_CONFIG_JSON: getPath(PROJECT_DIR, BASE_TS_CONFIG_FILE_NAME)
} as const;
return mockConstants;
}
67 changes: 47 additions & 20 deletions src/__testing__/mock/file-mock.utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,27 @@ import { MockConstants, FileMockConstants, DirMockConstants } from './constants'
import { AngularJson } from '../../angular';
import { CPUtilities, FsUtilities, JsonUtilities } from '../../encapsulation';
import { EnvUtilities } from '../../env';
import { TsConfigUtilities } from '../../tsconfig';
import { getPath } from '../../utilities';
import { WorkspaceUtilities } from '../../workspace';

export const defaultFilesToMock: (keyof FileMockConstants)[] = [
'WORKSPACE_JSON',
'BASE_TS_CONFIG_JSON',
'ENV',
'GLOBAL_ENV_MODEL',
'ROOT_PACKAGE_JSON',
'DOCKER_COMPOSE_YAML',
'DEV_DOCKER_COMPOSE_YAML',
'LOCAL_DOCKER_COMPOSE_YAML'
] as const;

export const defaultFoldersToMock: (keyof DirMockConstants)[] = [
'LIBS_DIR',
'APPS_DIR',
'PROJECT_DIR'
] as const;

export abstract class FileMockUtilities {

private static readonly mockMethodForFile: Record<
Expand All @@ -27,33 +45,27 @@ export abstract class FileMockUtilities {
ANGULAR_ENVIRONMENT: this.createEmptyFile,
TS_LIBRARY_PACKAGE_JSON: this.createEmptyFile,
ROOT_PACKAGE_JSON: this.createRootPackageJson,
ENV: this.createEmptyFile,
GLOBAL_ENV_MODEL: this.createGlobalEnvModel
ENV: this.createEnv,
GLOBAL_ENV_MODEL: this.createGlobalEnvModel,
WORKSPACE_JSON: WorkspaceUtilities.createConfig,
BASE_TS_CONFIG_JSON: TsConfigUtilities.createBaseTsConfig
};

static async setup(
mockConstants: MockConstants,
filesToMock: (keyof FileMockConstants)[] = [],
contentOverrides: Partial<Record<keyof FileMockConstants, string | string[]>> = {}
filesToMock: (keyof FileMockConstants)[] = defaultFilesToMock,
contentOverrides: Partial<Record<keyof FileMockConstants, string | string[]>> = {},
foldersToMock: (keyof DirMockConstants)[] = defaultFoldersToMock
): Promise<void> {
await FsUtilities.rm(getPath(mockConstants.PROJECT_DIR));
await FsUtilities.rm(mockConstants.PROJECT_DIR);
CPUtilities['cwd'] = mockConstants.PROJECT_DIR;
await this.mockFolders(mockConstants);
await this.mockFolders(foldersToMock, mockConstants);
await this.mockFiles(filesToMock, contentOverrides, mockConstants);
await WorkspaceUtilities.createConfig();
}

private static async mockFolders(mockConstants: MockConstants): Promise<void> {
const dirMockConstants: DirMockConstants = {
ANGULAR_APP_DIR: mockConstants.ANGULAR_APP_DIR,
APPS_DIR: mockConstants.APPS_DIR,
LIBS_DIR: mockConstants.LIBS_DIR,
PROJECT_DIR: mockConstants.PROJECT_DIR,
TS_LIBRARY_DIR: mockConstants.TS_LIBRARY_DIR,
GITHUB_WORKFLOW_DIR: mockConstants.GITHUB_WORKFLOW_DIR
};
for (const entry of Object.values(dirMockConstants)) {
await FsUtilities.mkdir(getPath(entry), true, false);
private static async mockFolders(foldersToMock: (keyof DirMockConstants)[], mockConstants: MockConstants): Promise<void> {
for (const entry of foldersToMock) {
await FsUtilities.mkdir(mockConstants[entry], true, false);
}
}

Expand Down Expand Up @@ -117,7 +129,7 @@ export abstract class FileMockUtilities {
private static async createRootPackageJson(mockConstants: MockConstants): Promise<void> {
await FsUtilities.createFile(mockConstants.ROOT_PACKAGE_JSON, [
'{',
' "name": "@library/library",',
' "name": "sandbox",',
' "version": "1.0.0",',
' "main": "index.js",',
' "scripts": {',
Expand All @@ -126,7 +138,18 @@ export abstract class FileMockUtilities {
' "keywords": [],',
' "author": "",',
' "license": "ISC",',
' "description": ""',
' "description": "",',
' "devDependencies": {',
' "autoprefixer": "^10.4.21",',
' "eslint": "^9.25.1",',
' "eslint-config-service-soft": "^2.0.8",',
' "postcss": "^8.5.3",',
' "tailwindcss": "^4.1.4"',
' },',
' "workspaces": [',
' "apps/*",',
' "libs/*"',
' ]',
'}'
], true, false);
}
Expand Down Expand Up @@ -176,4 +199,8 @@ export abstract class FileMockUtilities {
private static async createGlobalEnvModel(): Promise<void> {
await EnvUtilities['createGlobalEnvironmentModel']();
}

private static async createEnv(mockConstants: MockConstants): Promise<void> {
await FsUtilities.createFile(mockConstants.ENV, ['prod_root_domain=test.com', 'is_public=false']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { faker } from '@faker-js/faker';

const usedKeys: string[] = [];
export function fakeUniqueString(): string {
const key: string = faker.string.alpha({ length: { min: 1, max: 20 }, exclude: usedKeys });
const key: string = faker.string.alpha({ length: { min: 2, max: 20 }, exclude: usedKeys });
usedKeys.push(key);
return key;
}
6 changes: 3 additions & 3 deletions src/angular/angular-utilities.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { afterEach, beforeEach, describe, expect, jest, test } from '@jest/globals';

import { AngularUtilities } from './angular.utilities';
import { fakeAddNavElementConfig, fakeTsImportDefinition, FileMockUtilities, getMockConstants, MockConstants } from '../__testing__';
import { fakeAddNavElementConfig, fakeTsImportDefinition, FileMockUtilities, getMockConstants, MAX_GEN_CODE_TIME, MockConstants } from '../__testing__';
import { CPUtilities, FsUtilities } from '../encapsulation';
import { NpmUtilities } from '../npm';
import { TsImportDefinition } from '../ts';
Expand Down Expand Up @@ -255,7 +255,7 @@ describe('AngularUtilities', () => {
'',
'export const routes: NavRoute[] = NavUtilities.getAngularRoutes(navbarRows, footerRows, [notFoundRoute]);'
]);
}, 20000);
}, MAX_GEN_CODE_TIME);

test('generatePage for footer', async () => {
await AngularUtilities.setupNavigation(mockConstants.ANGULAR_APP_DIR, mockConstants.ANGULAR_APP_NAME);
Expand Down Expand Up @@ -319,7 +319,7 @@ describe('AngularUtilities', () => {
'',
'export const routes: NavRoute[] = NavUtilities.getAngularRoutes(navbarRows, footerRows, [notFoundRoute]);'
]);
}, 10000);
}, MAX_GEN_CODE_TIME);

test('addPwaSupport', async () => {
await AngularUtilities.setupPwa(mockConstants.ANGULAR_APP_DIR, mockConstants.ANGULAR_APP_NAME);
Expand Down
1 change: 0 additions & 1 deletion src/angular/angular.utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,6 @@ export abstract class AngularUtilities {
{ defaultImport: false, element: 'OfflineService', path: './services/offline.service' }
]
);
// TODO: enable OfflineRequestInterceptor. Need to fix parsing of provideServiceWorker first.
await this.addProvider(
root,
// eslint-disable-next-line typescript/no-unsafe-assignment, typescript/no-explicit-any
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { afterEach, beforeEach, describe, expect, jest, test } from '@jest/globals';

import { FileMockUtilities, getMockConstants, MAX_ADD_TIME, MockConstants, mockInquire } from '../../../__testing__';
import { InquirerUtilities } from '../../../encapsulation';
import { AddConfiguration, AddType } from '../models';
import { AddAngularLibraryCommand } from './add-angular-library.command';

const mockConstants: MockConstants = getMockConstants('add-angular-library-command');

describe('AddAngularLibraryCommand', () => {
beforeEach(async () => {
await FileMockUtilities.setup(mockConstants);
InquirerUtilities['inquire'] = jest.fn(mockInquire({
scope: '@sandbox'
}));
});

test('should run', async () => {
const baseConfig: AddConfiguration = { name: 'ui', type: AddType.ANGULAR_LIBRARY };
const command: AddAngularLibraryCommand = new AddAngularLibraryCommand(baseConfig);
await command.run();
expect(true).toBe(true);
}, MAX_ADD_TIME);

afterEach(() => {
jest.restoreAllMocks();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AngularUtilities } from '../../../angular';
import { ANGULAR_JSON_FILE_NAME, GIT_IGNORE_FILE_NAME, LIBS_DIRECTORY_NAME, PACKAGE_JSON_FILE_NAME } from '../../../constants';
import { ANGULAR_JSON_FILE_NAME, BASE_TS_CONFIG_FILE_NAME, GIT_IGNORE_FILE_NAME, LIBS_DIRECTORY_NAME, PACKAGE_JSON_FILE_NAME } from '../../../constants';
import { FsUtilities, JsonUtilities, QuestionsFor } from '../../../encapsulation';
import { EslintUtilities } from '../../../eslint';
import { NpmUtilities, PackageJson } from '../../../npm';
Expand Down Expand Up @@ -149,7 +149,7 @@ export class AddAngularLibraryCommand extends BaseAddCommand<AddAngularLibraryCo
console.log('sets up tsconfig');

await Promise.all([
TsConfigUtilities.updateTsConfig(config.name, { extends: '../../tsconfig.base.json' }),
TsConfigUtilities.updateTsConfig(config.name, { extends: `../../${BASE_TS_CONFIG_FILE_NAME}` }),
this.createTsConfigEslint(root),
this.updateTsConfigLib(root),
this.updateTsConfigSpec(root),
Expand Down Expand Up @@ -197,7 +197,7 @@ export class AddAngularLibraryCommand extends BaseAddCommand<AddAngularLibraryCo
outDir: './out-tsc/eslint',
types: ['jasmine']
},
extends: '../../tsconfig.base.json',
extends: `../../${BASE_TS_CONFIG_FILE_NAME}`,
files: ['src/public-api.ts'],
include: [
'src/**/*.spec.ts',
Expand Down
Loading