@digicroz/node-backend-kit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +255 -0
  2. package/dist/b2fPortalV3/b2fPortal.d.ts +2 -0
  3. package/dist/b2fPortalV3/b2fPortal.js +199 -0
  4. package/dist/b2fPortalV3/checkModuleRecords.d.ts +1 -0
  5. package/dist/b2fPortalV3/checkModuleRecords.js +60 -0
  6. package/dist/b2fPortalV3/checkModuleZodRecords.d.ts +1 -0
  7. package/dist/b2fPortalV3/checkModuleZodRecords.js +60 -0
  8. package/dist/b2fPortalV3/moduleFileCreator.d.ts +1 -0
  9. package/dist/b2fPortalV3/moduleFileCreator.js +42 -0
  10. package/dist/b2fPortalV3/moduleZodCreator.d.ts +1 -0
  11. package/dist/b2fPortalV3/moduleZodCreator.js +41 -0
  12. package/dist/b2fPortalV3/stripExtensions.d.ts +12 -0
  13. package/dist/b2fPortalV3/stripExtensions.js +51 -0
  14. package/dist/b2fPortalV3/syncProjectCommons.d.ts +8 -0
  15. package/dist/b2fPortalV3/syncProjectCommons.js +180 -0
  16. package/dist/bin/nbk.d.ts +2 -0
  17. package/dist/bin/nbk.js +83 -0
  18. package/dist/cli/addDevVersion.d.ts +1 -0
  19. package/dist/cli/addDevVersion.js +51 -0
  20. package/dist/cli/addProdVersion.d.ts +1 -0
  21. package/dist/cli/addProdVersion.js +71 -0
  22. package/dist/cli/createInitWorkspaceShellFile.d.ts +1 -0
  23. package/dist/cli/createInitWorkspaceShellFile.js +129 -0
  24. package/dist/cli/deleteAllRepos.d.ts +1 -0
  25. package/dist/cli/deleteAllRepos.js +62 -0
  26. package/dist/cli/deleteAndCopy.d.ts +6 -0
  27. package/dist/cli/deleteAndCopy.js +88 -0
  28. package/dist/cli/deployAllRepos.d.ts +1 -0
  29. package/dist/cli/deployAllRepos.js +202 -0
  30. package/dist/cli/fixConfigFile.d.ts +5 -0
  31. package/dist/cli/fixConfigFile.js +76 -0
  32. package/dist/cli/gitAcpAllRepos.d.ts +1 -0
  33. package/dist/cli/gitAcpAllRepos.js +156 -0
  34. package/dist/cli/gitPushAllRepos.d.ts +1 -0
  35. package/dist/cli/gitPushAllRepos.js +70 -0
  36. package/dist/cli/initConfig.d.ts +1 -0
  37. package/dist/cli/initConfig.js +23 -0
  38. package/dist/cli/transfer2Shared.d.ts +1 -0
  39. package/dist/cli/transfer2Shared.js +180 -0
  40. package/dist/configs/environment.d.ts +4 -0
  41. package/dist/configs/environment.js +7 -0
  42. package/dist/helpers/checkCrossProjectImports.d.ts +2 -0
  43. package/dist/helpers/checkCrossProjectImports.js +73 -0
  44. package/dist/index.d.ts +18 -0
  45. package/dist/index.js +47 -0
  46. package/dist/types.d.ts +34 -0
  47. package/dist/types.js +4 -0
  48. package/dist/utils/jsonFile.d.ts +7 -0
  49. package/dist/utils/jsonFile.js +24 -0
  50. package/dist/utils/loadConfig.d.ts +8 -0
  51. package/dist/utils/loadConfig.js +203 -0
  52. package/dist/utils/path.d.ts +6 -0
  53. package/dist/utils/path.js +16 -0
  54. package/dist/utils/progress.d.ts +65 -0
  55. package/dist/utils/progress.js +108 -0
  56. package/dist/utils/vscodeSettings.d.ts +1 -0
  57. package/dist/utils/vscodeSettings.js +67 -0
  58. package/nbk.schema.json +95 -0
  59. package/package.json +80 -0
@@ -0,0 +1,180 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { join } from "path";
4
+ import { clientRootDirPath, clientSrcDirPath } from "../utils/path.js";
5
+ import { simpleGit } from "simple-git";
6
+ import { deleteAndCopy } from "./deleteAndCopy.js";
7
+ import { loadConfig } from "../utils/loadConfig.js";
8
+ import { createMultiProgress, failProgress, startProgress, successProgress, } from "../utils/progress.js";
9
+ export const transfer2Shared = async () => {
10
+ // Load projects from the configuration file
11
+ const config = loadConfig();
12
+ // Export projects for use in CLI tools
13
+ let b2fPortalProjects = config.projects;
14
+ console.log("------------transfer2Shared Started-------------");
15
+ try {
16
+ const mainSpinner = startProgress("Checking main repository status");
17
+ const myGit = simpleGit();
18
+ const status = await myGit.status();
19
+ // Save the current branch name
20
+ const currentBranch = status.current;
21
+ // console.log(currentBranch);
22
+ if (currentBranch !== "main") {
23
+ failProgress(mainSpinner, `Current branch is ${currentBranch}, but it should be main.`);
24
+ throw new Error(`Current branch is ${currentBranch}, but it should be main.`);
25
+ }
26
+ if (status.files.length > 0) {
27
+ failProgress(mainSpinner, "❌ Uncommitted changes detected");
28
+ console.log("Commit or stash your changes before proceeding.");
29
+ process.exit(1);
30
+ }
31
+ // Pull the latest changes
32
+ mainSpinner.text = "Pulling latest changes from main repository";
33
+ await myGit.pull();
34
+ successProgress(mainSpinner, "Main repository checked successfully");
35
+ const repositoryPackageJsonPath = join(clientRootDirPath, "package.json");
36
+ if (!fs.existsSync(repositoryPackageJsonPath)) {
37
+ console.log("repositoryPackageJsonPath does not exist");
38
+ console.log({ repositoryPackageJsonPath });
39
+ return;
40
+ }
41
+ const repositoryPackageJson = JSON.parse(fs.readFileSync(repositoryPackageJsonPath, "utf8"));
42
+ if (!repositoryPackageJson.name) {
43
+ console.log("repositoryPackageJson.name does not exist");
44
+ console.log({ repositoryPackageJson });
45
+ return;
46
+ }
47
+ if (b2fPortalProjects.length === 0) {
48
+ console.log("No projects found in configuration");
49
+ return;
50
+ }
51
+ const projectProgress = createMultiProgress();
52
+ projectProgress.start("Processing projects");
53
+ projectProgress.setTotal(b2fPortalProjects.length);
54
+ for (const project of b2fPortalProjects) {
55
+ const projectSpinner = startProgress(`Processing project: ${project.projectName}`);
56
+ try {
57
+ const serverB2fPath = join(clientSrcDirPath, "serverB2f");
58
+ // console.log('serverB2fPath', serverB2fPath);
59
+ if (!fs.existsSync(serverB2fPath)) {
60
+ failProgress(projectSpinner, "serverB2fPath does not exist");
61
+ console.log({ serverB2fPath });
62
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
63
+ continue;
64
+ }
65
+ const projectSrcPath = join(clientSrcDirPath, project.projectBaseDirPath);
66
+ // console.log("projectSrcpath", projectSrcPath);
67
+ if (!fs.existsSync(projectSrcPath)) {
68
+ failProgress(projectSpinner, "projectSrcPath does not exist");
69
+ console.log({ projectSrcPath });
70
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
71
+ continue;
72
+ }
73
+ if (project.sharedBackendPath) {
74
+ projectSpinner.text = `Checking shared backend path for project: ${project.projectName}`;
75
+ const sharedBackendPath = join(clientRootDirPath, project.sharedBackendPath);
76
+ // console.log("sharedBackendPath", sharedBackendPath);
77
+ if (!fs.existsSync(sharedBackendPath)) {
78
+ failProgress(projectSpinner, "sharedBackendPath does not exist");
79
+ console.log({ sharedBackendPath });
80
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
81
+ continue;
82
+ }
83
+ const options = {
84
+ baseDir: path.resolve(sharedBackendPath),
85
+ binary: "git",
86
+ maxConcurrentProcesses: 6,
87
+ };
88
+ projectSpinner.text = `Checking shared backend repository status for project: ${project.projectName}`;
89
+ const sharedBackendGit = simpleGit(options);
90
+ let sharedBackendGitStatus = await sharedBackendGit.status();
91
+ // Save the current branch name
92
+ const currentBranch = sharedBackendGitStatus.current;
93
+ // console.log(currentBranch);
94
+ if (currentBranch !== "main") {
95
+ failProgress(projectSpinner, `Shared backend branch is ${currentBranch}, but it should be main.`);
96
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
97
+ throw new Error(` Shared backend branch is ${currentBranch}, but it should be main.`);
98
+ }
99
+ projectSpinner.text = `Pulling latest changes from shared backend repository for project: ${project.projectName}`;
100
+ await sharedBackendGit.pull();
101
+ const sharedProjectSrcPath = join(sharedBackendPath, "src", project.projectBaseDirPath);
102
+ // console.log("sharedProjectSrcPath", sharedProjectSrcPath);
103
+ if (!fs.existsSync(sharedProjectSrcPath)) {
104
+ failProgress(projectSpinner, "sharedProjectSrcPath does not exist");
105
+ console.log({ sharedProjectSrcPath });
106
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
107
+ continue;
108
+ }
109
+ projectSpinner.text = `Copying files for project: ${project.projectName}`;
110
+ await deleteAndCopy(projectSrcPath, sharedProjectSrcPath);
111
+ projectSpinner.text = `Checking for changes in shared backend repository for project: ${project.projectName}`;
112
+ sharedBackendGitStatus = await sharedBackendGit.status();
113
+ const sharedBackendChanges = sharedBackendGitStatus.files.some((file) => file.path.startsWith(`src/${project.projectBaseDirPath}/`));
114
+ if (!sharedBackendChanges) {
115
+ successProgress(projectSpinner, `No changes detected in the sharedBackend directory for project: ${project.projectName}`);
116
+ projectProgress.incrementCompleted(`Completed: ${project.projectName} - No changes`);
117
+ continue;
118
+ }
119
+ const repositoryCommitJsonPath = join(sharedBackendPath, "commit.json");
120
+ if (!fs.existsSync(repositoryCommitJsonPath)) {
121
+ failProgress(projectSpinner, "repositoryCommitJsonPath does not exist");
122
+ console.log({ sharedBackendPath });
123
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
124
+ return;
125
+ }
126
+ const repositoryCommitJson = JSON.parse(fs.readFileSync(repositoryCommitJsonPath, "utf8"));
127
+ if (!repositoryCommitJson.last_commit) {
128
+ console.log("repositoryCommitJson.last_commit does not exist");
129
+ console.log({ clientRootDirPath });
130
+ }
131
+ const currentDate = new Date().toLocaleString();
132
+ // Update last_commit
133
+ repositoryCommitJson.last_commit = currentDate;
134
+ // Write the updated JSON back to the file
135
+ fs.writeFileSync(repositoryCommitJsonPath, JSON.stringify(repositoryCommitJson, null, 4), "utf8");
136
+ projectSpinner.text = `Committing changes to shared backend repository for project: ${project.projectName}`;
137
+ await sharedBackendGit.add(`commit.json`);
138
+ // Add only the b2fPortal folder
139
+ await sharedBackendGit.add(`src/${project.projectBaseDirPath}/*`);
140
+ // Commit with the current date and time
141
+ const commitMessage = `${currentDate} transfer2Shared from ${repositoryPackageJson.name}`;
142
+ await sharedBackendGit.commit(commitMessage);
143
+ // Push the commit to the main branch
144
+ projectSpinner.text = `Pushing changes to shared backend repository for project: ${project.projectName}`;
145
+ await sharedBackendGit.push("origin", "main");
146
+ successProgress(projectSpinner, `Changes committed and pushed successfully for project: ${project.projectName}`);
147
+ projectProgress.incrementCompleted(`Completed: ${project.projectName}`);
148
+ }
149
+ else {
150
+ failProgress(projectSpinner, "sharedBackendPath is not defined");
151
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
152
+ console.error("sharedBackendPath is not defined");
153
+ process.exit(1);
154
+ }
155
+ if (project.sections.length === 0) {
156
+ console.log("no sections in " + project.projectName);
157
+ }
158
+ }
159
+ catch (error) {
160
+ failProgress(projectSpinner, `Error processing project: ${project.projectName}`);
161
+ projectProgress.incrementFailed(`Failed: ${project.projectName}`);
162
+ console.error(`Error processing project ${project.projectName}:`, error);
163
+ }
164
+ }
165
+ projectProgress.complete();
166
+ }
167
+ catch (error) {
168
+ console.error("Error while committing changes:", error.message);
169
+ // Handle edge case where the repository might have conflicting changes
170
+ if (error.message.includes("CONFLICT")) {
171
+ console.error("Merge conflict detected. Please resolve conflicts manually.");
172
+ }
173
+ }
174
+ console.log("------------transfer2Shared finished-------------");
175
+ };
176
+ // if (isDeveloperAdarsh) {
177
+ // transfer2Shared();
178
+ // } else {
179
+ // console.log("This command can only be run by authorized developers.");
180
+ // }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Environment configuration for nbk
3
+ */
4
+ export declare const isDeveloperAdarsh: boolean;
@@ -0,0 +1,7 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ /**
4
+ * Environment configuration for nbk
5
+ */
6
+ // Flag to determine if the current user is Adarsh (for restricted operations)
7
+ export const isDeveloperAdarsh = process.env.DEVELOPER_NAME === "adarsh";
@@ -0,0 +1,2 @@
1
+ import { TNbkProject } from "../types.js";
2
+ export declare function checkCrossProjectImportsFun(b2fPortalProjects: TNbkProject[]): Promise<void>;
@@ -0,0 +1,73 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ // Helper function to find all .ts files in a directory (including subdirectories)
4
+ async function findTsFilesInDir(directoryPath) {
5
+ const files = [];
6
+ const direntArr = await fs.promises.readdir(directoryPath, {
7
+ withFileTypes: true,
8
+ });
9
+ for (const dirent of direntArr) {
10
+ const fullPath = path.join(directoryPath, dirent.name);
11
+ if (dirent.isDirectory()) {
12
+ // Recursively search in subdirectories
13
+ files.push(...(await findTsFilesInDir(fullPath)));
14
+ }
15
+ else if (dirent.isFile() && dirent.name.endsWith(".ts")) {
16
+ // Add .ts files to the result
17
+ files.push(fullPath);
18
+ }
19
+ }
20
+ return files;
21
+ }
22
+ // Main function to validate project imports
23
+ export async function checkCrossProjectImportsFun(b2fPortalProjects) {
24
+ console.log("------------checkCrossProjectImports Started-------------");
25
+ let clientRootDirPath = process.cwd();
26
+ // Track number of cross-project import violations
27
+ let crossProjectImportCount = 0;
28
+ // console.log({clientRootDirPath});
29
+ const srcDirPath = path.join(clientRootDirPath, "src");
30
+ const distDirPath = path.join(clientRootDirPath, "dist");
31
+ // Collect all project names
32
+ const projectNames = b2fPortalProjects.map((project) => project.projectName);
33
+ // Process each project
34
+ for (const project of b2fPortalProjects) {
35
+ const projectDir = path.resolve(srcDirPath, project.projectBaseDirPath);
36
+ // console.log("Project directory:", projectDir);
37
+ // Find all .ts files in the project directory (including subdirectories)
38
+ const tsFiles = await findTsFilesInDir(projectDir);
39
+ // console.log("Found .ts files:", tsFiles);
40
+ // Process each TypeScript file
41
+ for (const filePath of tsFiles) {
42
+ // // console.log("Found file:", filePath); // Log the file being processed
43
+ const content = await fs.promises.readFile(filePath, "utf-8");
44
+ // // console.log("File content:", content); // Log the content of the file
45
+ const importRegex = /import\s+.*?from\s+['"]([^'"]+)['"]/g;
46
+ let match;
47
+ while ((match = importRegex.exec(content)) !== null) {
48
+ const importPath = match[1];
49
+ // console.log("Import path:", importPath); // Log the import path found
50
+ // Skip relative imports and external modules
51
+ if (importPath.startsWith(".") || !importPath.includes("/")) {
52
+ continue;
53
+ }
54
+ // Check if the import path includes another project's name
55
+ const isCrossProjectImport = projectNames.some((otherProjectName) => otherProjectName !== project.projectName &&
56
+ importPath.includes(otherProjectName));
57
+ if (isCrossProjectImport) {
58
+ crossProjectImportCount++;
59
+ console.error(`\n[ERROR] Invalid import detected in project "${project.projectName}":\n` +
60
+ ` - File: "${filePath}"\n` +
61
+ ` - Import Path: "${importPath}"\n`);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ console.log(`------------checkCrossProjectImports Finished-------------`);
67
+ if (crossProjectImportCount > 0) {
68
+ console.log(`Found ${crossProjectImportCount} cross-project import violations.`);
69
+ }
70
+ else {
71
+ console.log("No cross-project import violations detected.");
72
+ }
73
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Main entry point for the package
3
+ */
4
+ import { TNbkConfig } from "./types.js";
5
+ export declare function greet(name: string): string;
6
+ /**
7
+ * Initializes the nbk by loading configuration from a JSON file.
8
+ * This is the primary initialization method for the library.
9
+ *
10
+ * @param {Object} options - Initialization options
11
+ * @param {string} [options.configPath] - Path to the JSON config file (defaults to nbk.config.json in current directory)
12
+ * @returns {TNbkConfig} The loaded and validated configuration
13
+ * @throws {Error} If the configuration is invalid or file doesn't exist
14
+ */
15
+ export declare function nbkInit(options?: {
16
+ configPath?: string;
17
+ }): Promise<TNbkConfig>;
18
+ export * from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Main entry point for the package
3
+ */
4
+ import * as path from "path";
5
+ import { b2fPortalInit } from "./b2fPortalV3/b2fPortal.js";
6
+ import { checkCrossProjectImportsFun } from "./helpers/checkCrossProjectImports.js";
7
+ import { loadConfig } from "./utils/loadConfig.js";
8
+ export function greet(name) {
9
+ return `Hello, ${name}!`;
10
+ }
11
+ /**
12
+ * Initializes the nbk by loading configuration from a JSON file.
13
+ * This is the primary initialization method for the library.
14
+ *
15
+ * @param {Object} options - Initialization options
16
+ * @param {string} [options.configPath] - Path to the JSON config file (defaults to nbk.config.json in current directory)
17
+ * @returns {TNbkConfig} The loaded and validated configuration
18
+ * @throws {Error} If the configuration is invalid or file doesn't exist
19
+ */
20
+ export async function nbkInit(options = {}) {
21
+ // Set default options
22
+ const { configPath = path.join(process.cwd(), "nbk.config.json") } = options;
23
+ try {
24
+ // Load and validate config
25
+ const config = loadConfig(configPath);
26
+ if (!config) {
27
+ return {};
28
+ }
29
+ console.log(`Successfully loaded configuration with ${config.projects.length} projects`);
30
+ // Initialize features based on config flags
31
+ if (config.b2fPortal) {
32
+ await b2fPortalInit(config.projects);
33
+ }
34
+ if (config.checkCrossProjectImports) {
35
+ await checkCrossProjectImportsFun(config.projects);
36
+ }
37
+ return config;
38
+ }
39
+ catch (error) {
40
+ console.error("Failed to initialize nbk:", error);
41
+ throw error;
42
+ }
43
+ }
44
+ // When using ES modules with TypeScript, you need to include the .js extension
45
+ // in import statements, even though the source file has a .ts extension
46
+ // This is because Node.js will look for .js files at runtime
47
+ export * from "./types.js";
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Type definitions for nbk
3
+ */
4
+ /**
5
+ * Represents a section within a project
6
+ */
7
+ export type TNbkProjectSection = {
8
+ sectionName: string;
9
+ repository: {
10
+ name: string;
11
+ path: string;
12
+ };
13
+ localPath: string;
14
+ isZodCreator: boolean;
15
+ needNextJsPatch?: boolean;
16
+ };
17
+ /**
18
+ * Represents a project in the configuration
19
+ */
20
+ export type TNbkProject = {
21
+ projectName: string;
22
+ projectBaseDirPath: string;
23
+ sharedBackendPath?: string;
24
+ sections: TNbkProjectSection[];
25
+ };
26
+ /**
27
+ * Type for nbk.config.ts file's default export,
28
+ * which is an array of projects
29
+ */
30
+ export type TNbkConfig = {
31
+ projects: TNbkProject[];
32
+ b2fPortal: boolean;
33
+ checkCrossProjectImports: boolean;
34
+ };
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type definitions for nbk
3
+ */
4
+ export {};
@@ -0,0 +1,7 @@
1
+ export declare function cleanTrailingCommas(jsonString: string): string;
2
+ /**
3
+ * Strips comments from a JSON string
4
+ * @param jsonString - The JSON string that may contain comments
5
+ * @returns JSON string with comments removed
6
+ */
7
+ export declare function stripComments(jsonString: string): string;
@@ -0,0 +1,24 @@
1
+ export function cleanTrailingCommas(jsonString) {
2
+ // Remove trailing commas in objects and arrays
3
+ return jsonString
4
+ // Remove trailing commas in objects
5
+ .replace(/,(\s*[}\]])/g, '$1')
6
+ // Remove trailing commas in arrays
7
+ .replace(/,(\s*])/g, '$1')
8
+ // Remove trailing commas after property values
9
+ .replace(/,(\s*})/g, '$1');
10
+ }
11
+ /**
12
+ * Strips comments from a JSON string
13
+ * @param jsonString - The JSON string that may contain comments
14
+ * @returns JSON string with comments removed
15
+ */
16
+ export function stripComments(jsonString) {
17
+ // Remove single line comments
18
+ let stripped = jsonString.replace(/\/\/.*$/gm, '');
19
+ // Remove multi-line comments
20
+ stripped = stripped.replace(/\/\*[\s\S]*?\*\//g, '');
21
+ // Remove trailing commas before closing braces/brackets
22
+ stripped = stripped.replace(/,(\s*[}\]])/g, '$1');
23
+ return stripped;
24
+ }
@@ -0,0 +1,8 @@
1
+ import { TNbkConfig } from "../types.js";
2
+ /**
3
+ * Loads and validates the NBK configuration from a JSON file
4
+ * @param configPath - Path to the config file (optional, defaults to nbk.config.json in current directory)
5
+ * @returns Validated TNbkConfig object
6
+ * @throws Error if the configuration is invalid or file doesn't exist
7
+ */
8
+ export declare function loadConfig(configPath?: string): TNbkConfig;
@@ -0,0 +1,203 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { stripComments } from "./jsonFile.js";
4
+ /**
5
+ * Creates a visually distinct, colorful error message for config file not found errors
6
+ * @param configPath - Path to the config file that was not found
7
+ * @returns Formatted error message string
8
+ */
9
+ function createConfigNotFoundErrorMessage(configPath) {
10
+ const separator = "=".repeat(80);
11
+ const title = "CONFIG FILE NOT FOUND ERROR";
12
+ // Color codes for terminal
13
+ const red = "\x1b[31m";
14
+ const yellow = "\x1b[33m";
15
+ const cyan = "\x1b[36m";
16
+ const reset = "\x1b[0m";
17
+ const bold = "\x1b[1m";
18
+ // Create suggested config example
19
+ const configExample = JSON.stringify({
20
+ projects: [
21
+ {
22
+ projectName: "Your Project",
23
+ projectBaseDirPath: "C:/path/to/your/project",
24
+ sections: [
25
+ {
26
+ sectionName: "Backend",
27
+ repository: {
28
+ name: "your-backend-repo",
29
+ path: "https://github.com/your-org/your-repo",
30
+ },
31
+ localPath: "C:/path/to/your/backend",
32
+ isZodCreator: true,
33
+ },
34
+ ],
35
+ },
36
+ ],
37
+ b2fPortal: false,
38
+ checkCrossProjectImports: true,
39
+ }, null, 2);
40
+ return `
41
+ ${red}${separator}${reset}
42
+ ${red}${bold} ${title} ${reset}
43
+ ${red}${separator}${reset}
44
+
45
+ ${yellow}The configuration file could not be found at:${reset}
46
+ ${cyan}${configPath}${reset}
47
+
48
+ ${yellow}Please create a nbk.config.json file at the root of your project with the following structure:${reset}
49
+
50
+ ${configExample}
51
+
52
+ ${yellow}To specify a different config location, provide the path when calling nbkInit():${reset}
53
+ ${cyan}nbkInit({ configPath: './path/to/your/config.json' })${reset}
54
+
55
+ ${red}${separator}${reset}
56
+ `;
57
+ }
58
+ /**
59
+ * Creates a visually distinct, colorful error message for validation errors
60
+ * @param message - The error message content
61
+ * @returns Formatted error message string
62
+ */
63
+ function createValidationErrorMessage(message) {
64
+ const separator = "=".repeat(80);
65
+ const title = "CONFIG VALIDATION ERROR";
66
+ // Color codes for terminal
67
+ const red = "\x1b[31m";
68
+ const yellow = "\x1b[33m";
69
+ const green = "\x1b[32m";
70
+ const reset = "\x1b[0m";
71
+ const bold = "\x1b[1m";
72
+ return `
73
+ ${red}${separator}${reset}
74
+ ${red}${bold} ${title} ${reset}
75
+ ${red}${separator}${reset}
76
+
77
+ ${yellow}${message}${reset}
78
+
79
+ ${green}Please review your configuration file and fix the validation issue.${reset}
80
+
81
+ ${red}${separator}${reset}
82
+ `;
83
+ }
84
+ /**
85
+ * Loads and validates the NBK configuration from a JSON file
86
+ * @param configPath - Path to the config file (optional, defaults to nbk.config.json in current directory)
87
+ * @returns Validated TNbkConfig object
88
+ * @throws Error if the configuration is invalid or file doesn't exist
89
+ */
90
+ export function loadConfig(configPath = path.join(process.cwd(), "nbk.config.json")) {
91
+ try {
92
+ // Check if file exists
93
+ if (!fs.existsSync(configPath)) {
94
+ const errorMessage = createConfigNotFoundErrorMessage(configPath);
95
+ console.error(errorMessage);
96
+ throw new Error(`Configuration file not found: ${configPath}`);
97
+ }
98
+ // Read file and strip comments
99
+ const configString = fs.readFileSync(configPath, "utf8");
100
+ const strippedConfig = stripComments(configString);
101
+ // Parse JSON file
102
+ const config = JSON.parse(strippedConfig);
103
+ // Validate the entire config structure
104
+ try {
105
+ validateConfig(config);
106
+ }
107
+ catch (validationError) {
108
+ if (validationError instanceof Error) {
109
+ const errorMessage = createValidationErrorMessage(validationError.message);
110
+ console.error(errorMessage);
111
+ }
112
+ throw validationError;
113
+ }
114
+ return config;
115
+ }
116
+ catch (error) {
117
+ if (error instanceof SyntaxError) {
118
+ const errorMessage = createValidationErrorMessage(`Invalid JSON in configuration file: ${error.message}`);
119
+ console.error(errorMessage);
120
+ throw new Error(`Invalid JSON in configuration file: ${error.message}`);
121
+ }
122
+ throw error;
123
+ }
124
+ }
125
+ /**
126
+ * Validates the configuration object
127
+ * @param config - The configuration object to validate
128
+ * @throws Error with detailed message if validation fails
129
+ */
130
+ function validateConfig(config) {
131
+ // Check if config has projects array
132
+ if (!config || !Array.isArray(config.projects)) {
133
+ throw new Error('Configuration must contain a "projects" array');
134
+ }
135
+ // Check for b2fPortal and checkCrossProjectImports fields
136
+ if (typeof config.b2fPortal !== "boolean") {
137
+ throw new Error('Configuration must contain a "b2fPortal" boolean field');
138
+ }
139
+ if (typeof config.checkCrossProjectImports !== "boolean") {
140
+ throw new Error('Configuration must contain a "checkCrossProjectImports" boolean field');
141
+ }
142
+ // Validate each project
143
+ for (let i = 0; i < config.projects.length; i++) {
144
+ const project = config.projects[i];
145
+ validateProject(project, i);
146
+ }
147
+ }
148
+ /**
149
+ * Validates a project configuration
150
+ * @param project - The project to validate
151
+ * @param index - Index in projects array for error messages
152
+ */
153
+ function validateProject(project, index) {
154
+ // Required fields
155
+ if (!project.projectName) {
156
+ throw new Error(`Project at index ${index} is missing "projectName"`);
157
+ }
158
+ if (!project.projectBaseDirPath) {
159
+ throw new Error(`Project "${project.projectName}" is missing "projectBaseDirPath"`);
160
+ }
161
+ if (!Array.isArray(project.sections)) {
162
+ throw new Error(`Project "${project.projectName}" must contain a "sections" array`);
163
+ }
164
+ // Validate each section
165
+ for (let j = 0; j < project.sections.length; j++) {
166
+ validateSection(project.sections[j], project.projectName, j);
167
+ }
168
+ }
169
+ /**
170
+ * Validates a project section configuration
171
+ * @param section - The section to validate
172
+ * @param projectName - Parent project name for error messages
173
+ * @param index - Index in sections array for error messages
174
+ */
175
+ function validateSection(section, projectName, index) {
176
+ // Required fields
177
+ if (!section.sectionName) {
178
+ throw new Error(`Section at index ${index} in project "${projectName}" is missing "sectionName"`);
179
+ }
180
+ if (!section.repository) {
181
+ throw new Error(`Section "${section.sectionName}" in project "${projectName}" is missing "repository"`);
182
+ }
183
+ if (!section.repository.name) {
184
+ throw new Error(`Section "${section.sectionName}" in project "${projectName}" has repository missing "name"`);
185
+ }
186
+ if (!section.repository.path) {
187
+ throw new Error(`Section "${section.sectionName}" in project "${projectName}" has repository missing "path"`);
188
+ }
189
+ if (!section.localPath) {
190
+ throw new Error(`Section "${section.sectionName}" in project "${projectName}" is missing "localPath"`);
191
+ }
192
+ if (section.isZodCreator === undefined) {
193
+ throw new Error(`Section "${section.sectionName}" in project "${projectName}" is missing "isZodCreator"`);
194
+ }
195
+ // Type checks
196
+ if (typeof section.isZodCreator !== "boolean") {
197
+ throw new Error(`Section "${section.sectionName}" in project "${projectName}": "isZodCreator" must be a boolean`);
198
+ }
199
+ if (section.needNextJsPatch !== undefined &&
200
+ typeof section.needNextJsPatch !== "boolean") {
201
+ throw new Error(`Section "${section.sectionName}" in project "${projectName}": "needNextJsPatch" must be a boolean if provided`);
202
+ }
203
+ }
@@ -0,0 +1,6 @@
1
+ export declare const packageRootDirPath: string;
2
+ export declare const packageSrcDirPath: string;
3
+ export declare const packageDistDirPath: string;
4
+ export declare const clientRootDirPath: string;
5
+ export declare const clientSrcDirPath: string;
6
+ export declare const clientDistDirPath: string;
@@ -0,0 +1,16 @@
1
+ import { join } from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname } from 'path';
4
+ /**
5
+ * Utility functions for working with paths in the backend kit
6
+ */
7
+ // Get the current file's directory
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ // Calculate common paths
11
+ export const packageRootDirPath = join(__dirname, '..', '..');
12
+ export const packageSrcDirPath = join(packageRootDirPath, 'src');
13
+ export const packageDistDirPath = join(packageRootDirPath, 'dist');
14
+ export const clientRootDirPath = process.cwd();
15
+ export const clientSrcDirPath = join(clientRootDirPath, 'src');
16
+ export const clientDistDirPath = join(clientRootDirPath, 'dist');