Skip to content

Commit b1f94a3

Browse files
committed
feat(nx-devkit): initialize docker and project utils
1 parent df076da commit b1f94a3

17 files changed

+387
-1
lines changed

packages/nx-devkit/.swcrc

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"jsc": {
3+
"target": "es2022",
4+
"parser": {
5+
"syntax": "typescript",
6+
"decorators": true,
7+
"dynamicImport": true
8+
},
9+
"transform": {
10+
"decoratorMetadata": true,
11+
"legacyDecorator": true
12+
},
13+
"keepClassNames": true,
14+
"externalHelpers": true,
15+
"loose": true
16+
},
17+
"module": {
18+
"type": "commonjs"
19+
},
20+
"sourceMaps": true,
21+
"exclude": [
22+
"jest.config.ts",
23+
".*\\.spec.tsx?$",
24+
".*\\.test.tsx?$",
25+
"./src/jest-setup.ts$",
26+
"./**/jest-setup.ts$",
27+
".*.js$"
28+
]
29+
}

packages/nx-devkit/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# nx-devkit
2+
3+
This library was generated with [Nx](https://nx.dev).
4+
5+
## Building
6+
7+
Run `nx build nx-devkit` to build the library.
8+
9+
## Running unit tests
10+
11+
Run `nx test nx-devkit` to execute the unit tests via [Jest](https://jestjs.io).

packages/nx-devkit/eslint.config.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const baseConfig = require('../../eslint.config.js');
2+
3+
module.exports = [
4+
...baseConfig,
5+
{
6+
files: ['**/*.json'],
7+
rules: {
8+
'@nx/dependency-checks': [
9+
'error',
10+
{
11+
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
12+
},
13+
],
14+
},
15+
languageOptions: {
16+
parser: require('jsonc-eslint-parser'),
17+
},
18+
},
19+
];

packages/nx-devkit/jest.config.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { readFileSync } from 'fs';
2+
3+
// Reading the SWC compilation config and remove the "exclude"
4+
// for the test files to be compiled by SWC
5+
const { exclude: _, ...swcJestConfig } = JSON.parse(readFileSync(`${__dirname}/.swcrc`, 'utf-8'));
6+
7+
// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves.
8+
// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude"
9+
if (swcJestConfig.swcrc === undefined) {
10+
swcJestConfig.swcrc = false;
11+
}
12+
13+
// Uncomment if using global setup/teardown files being transformed via swc
14+
// https://nx.dev/nx-api/jest/documents/overview#global-setupteardown-with-nx-libraries
15+
// jest needs EsModule Interop to find the default exported setup/teardown functions
16+
// swcJestConfig.module.noInterop = false;
17+
18+
export default {
19+
displayName: 'nx-devkit',
20+
preset: '../../jest.preset.js',
21+
transform: {
22+
'^.+\\.[tj]s$': ['@swc/jest', swcJestConfig],
23+
},
24+
moduleFileExtensions: ['ts', 'js', 'html'],
25+
testEnvironment: 'node',
26+
coverageDirectory: '../../coverage/packages/nx-devkit',
27+
};

packages/nx-devkit/package.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@ebizbase/nx-devkit",
3+
"version": "0.1.0",
4+
"private": false,
5+
"description": "The nx devkit utils",
6+
"bugs": "https://github.com/ebizbase/dev-infras/issues",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/ebizbase/dev-infras.git"
10+
},
11+
"license": "MIT",
12+
"type": "commonjs",
13+
"main": "./src/index.js",
14+
"typings": "./src/index.d.ts",
15+
"dependencies": {
16+
"@nx/devkit": "^20.0.0",
17+
"tslib": "^2.3.0"
18+
},
19+
"publishConfig": {
20+
"access": "public"
21+
}
22+
}

packages/nx-devkit/project.json

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "nx-devkit",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "packages/nx-devkit/src",
5+
"projectType": "library",
6+
"tags": [],
7+
"targets": {
8+
"build": {
9+
"executor": "@nx/js:tsc",
10+
"outputs": ["{options.outputPath}"],
11+
"options": {
12+
"outputPath": "dist/packages/nx-devkit",
13+
"main": "packages/nx-devkit/src/index.ts",
14+
"tsConfig": "packages/nx-devkit/tsconfig.lib.json",
15+
"assets": ["packages/nx-devkit/.npmrc", "packages/nx-devkit/*.md"]
16+
}
17+
},
18+
"publish": {
19+
"dependsOn": ["build"],
20+
"executor": "nx:run-commands",
21+
"options": {
22+
"cwd": "dist/packages/nx-devkit",
23+
"command": "npm publish"
24+
}
25+
},
26+
"version": {
27+
"executor": "@jscutlery/semver:version",
28+
"options": {
29+
"preset": "angular",
30+
"trackDeps": true
31+
}
32+
}
33+
}
34+
}

packages/nx-devkit/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './lib/docker.utils';
2+
export * from './lib/project.utils';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { DockerUtils } from './docker.utils';
2+
import { execFileSync } from 'child_process';
3+
4+
// Mock execFileSync function
5+
jest.mock('child_process', () => ({
6+
execFileSync: jest.fn(),
7+
}));
8+
9+
describe('DockerService', () => {
10+
let dockerService: DockerUtils;
11+
12+
beforeEach(() => {
13+
dockerService = new DockerUtils();
14+
jest.resetAllMocks();
15+
});
16+
17+
describe('checkDockerInstalled', () => {
18+
it('should return true if Docker is installed', () => {
19+
// Arrange: execFileSync sẽ không ném ra lỗi
20+
(execFileSync as jest.Mock).mockImplementation(() => true);
21+
22+
// Act
23+
const result = dockerService.checkDockerInstalled(true);
24+
25+
// Assert
26+
expect(result).toBe(true);
27+
expect(execFileSync).toHaveBeenCalledWith('docker', ['info'], { stdio: 'inherit' });
28+
});
29+
30+
it('should return false if Docker is not installed or daemon is not running', () => {
31+
// Arrange: execFileSync sẽ ném ra lỗi
32+
(execFileSync as jest.Mock).mockImplementation(() => {
33+
throw new Error();
34+
});
35+
36+
// Act
37+
const result = dockerService.checkDockerInstalled(false);
38+
39+
// Assert
40+
expect(result).toBe(false);
41+
expect(execFileSync).toHaveBeenCalledWith('docker', ['info'], { stdio: 'ignore' });
42+
});
43+
});
44+
45+
describe('checkBuildxInstalled', () => {
46+
it('should return true if Docker buildx is installed', () => {
47+
// Arrange
48+
(execFileSync as jest.Mock).mockImplementation(() => true);
49+
50+
// Act
51+
const result = dockerService.checkBuildxInstalled(true);
52+
53+
// Assert
54+
expect(result).toBe(true);
55+
expect(execFileSync).toHaveBeenCalledWith('docker', ['buildx', 'version'], {
56+
stdio: 'inherit',
57+
});
58+
});
59+
60+
it('should return false if Docker buildx is not installed', () => {
61+
// Arrange
62+
(execFileSync as jest.Mock).mockImplementation(() => {
63+
throw new Error();
64+
});
65+
66+
// Act
67+
const result = dockerService.checkBuildxInstalled(false);
68+
69+
// Assert
70+
expect(result).toBe(false);
71+
expect(execFileSync).toHaveBeenCalledWith('docker', ['buildx', 'version'], {
72+
stdio: 'ignore',
73+
});
74+
});
75+
});
76+
});
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { execFileSync } from 'child_process';
2+
3+
export class DockerUtils {
4+
5+
6+
checkDockerInstalled(verbose: boolean): boolean {
7+
try {
8+
execFileSync('docker', ['info'], { stdio: verbose ? 'inherit' : 'ignore' });
9+
return true;
10+
} catch {
11+
return false;
12+
}
13+
}
14+
15+
checkBuildxInstalled(verbose: boolean): boolean {
16+
try {
17+
execFileSync('docker', ['buildx', 'version'], { stdio: verbose ? 'inherit' : 'ignore' });
18+
return true;
19+
} catch {
20+
return false;
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { ExecutorContext } from '@nx/devkit';
2+
import { ProjectUtils } from './project.utils';
3+
4+
describe('ProjectUtils', () => {
5+
let projectUtils: ProjectUtils;
6+
const context: ExecutorContext = {
7+
isVerbose: false,
8+
projectName: 'test-project',
9+
projectsConfigurations: {
10+
version: 1,
11+
projects: {
12+
'test-project': {
13+
root: '/root/test-project',
14+
},
15+
},
16+
},
17+
root: '/root',
18+
nxJsonConfiguration: {},
19+
cwd: '/root',
20+
projectGraph: {
21+
nodes: {},
22+
dependencies: {},
23+
},
24+
};
25+
26+
beforeEach(() => {
27+
projectUtils = new ProjectUtils(context);
28+
});
29+
30+
describe('constructor', () => {
31+
it('should throw an error if project name is not provided', () => {
32+
// Arrange: create context without projectName
33+
const invalidContext = { ...context, projectName: undefined };
34+
35+
// Act & Assert
36+
expect(() => new ProjectUtils(invalidContext as ExecutorContext)).toThrow(
37+
'No project name provided'
38+
);
39+
});
40+
});
41+
42+
describe('getProjectName', () => {
43+
it('should return the project name', () => {
44+
// Act
45+
const projectName = projectUtils.getProjectName();
46+
47+
// Assert
48+
expect(projectName).toBe('test-project');
49+
});
50+
});
51+
52+
describe('getProjectConfig', () => {
53+
it('should return the project configuration', () => {
54+
// Act
55+
const projectConfig = projectUtils.getProjectConfig();
56+
57+
// Assert
58+
expect(projectConfig).toEqual({ root: '/root/test-project' });
59+
});
60+
});
61+
62+
describe('getProjectRoot', () => {
63+
it('should return the project root path', () => {
64+
// Act
65+
const projectRoot = projectUtils.getProjectRoot();
66+
67+
// Assert
68+
expect(projectRoot).toBe('/root/test-project');
69+
});
70+
});
71+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ExecutorContext, ProjectConfiguration } from '@nx/devkit';
2+
3+
export class ProjectUtils {
4+
private readonly projectName: string;
5+
6+
constructor(private readonly context: ExecutorContext) {
7+
const projectName = context.projectName;
8+
if (!projectName) {
9+
throw new Error('No project name provided');
10+
}
11+
this.projectName = projectName;
12+
}
13+
14+
public getProjectName(): string {
15+
return this.projectName;
16+
}
17+
18+
public getProjectConfig(): ProjectConfiguration {
19+
return this.context.projectsConfigurations.projects[this.projectName];
20+
}
21+
22+
public getProjectRoot(): string {
23+
return this.getProjectConfig().root;
24+
}
25+
26+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
27+
public getMetadata(): { [key: string]: any } | undefined {
28+
return this.getProjectConfig().metadata;
29+
}
30+
}

packages/nx-devkit/tsconfig.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"module": "commonjs"
5+
},
6+
"files": [],
7+
"include": [],
8+
"references": [
9+
{
10+
"path": "./tsconfig.lib.json"
11+
},
12+
{
13+
"path": "./tsconfig.spec.json"
14+
}
15+
]
16+
}

packages/nx-devkit/tsconfig.lib.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "../../dist/out-tsc",
5+
"declaration": true,
6+
"types": ["node"]
7+
},
8+
"include": ["src/**/*.ts"],
9+
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
10+
}

packages/nx-devkit/tsconfig.spec.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "../../dist/out-tsc",
5+
"module": "commonjs",
6+
"types": ["jest", "node"]
7+
},
8+
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
9+
}

0 commit comments

Comments
 (0)