fix: separate client and server utilities to fix build error
Split lib/utils.ts into: - lib/utils.ts: Client-safe utilities only (no Node.js imports) - lib/project-utils.ts: Server-only utilities with Node.js imports This fixes the Next.js build error where client components were importing modules that contained Node.js dependencies (fs, path). All tests pass and build succeeds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import { GET } from '../projects/route';
|
|||||||
import { getProjects } from '@/lib/fs-utils';
|
import { getProjects } from '@/lib/fs-utils';
|
||||||
|
|
||||||
jest.mock('@/lib/fs-utils');
|
jest.mock('@/lib/fs-utils');
|
||||||
jest.mock('@/lib/utils', () => ({
|
jest.mock('@/lib/project-utils', () => ({
|
||||||
getProjectDisplayName: jest.fn((name) => name === 'FT' ? '外放构建' : name),
|
getProjectDisplayName: jest.fn((name) => name === 'FT' ? '外放构建' : name),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// app/api/projects/route.ts
|
// app/api/projects/route.ts
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { getProjects } from '@/lib/fs-utils';
|
import { getProjects } from '@/lib/fs-utils';
|
||||||
import { getProjectDisplayName } from '@/lib/utils';
|
import { getProjectDisplayName } from '@/lib/project-utils';
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
44
lib/__tests__/project-utils.test.ts
Normal file
44
lib/__tests__/project-utils.test.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { getProjectDisplayName } from '../project-utils';
|
||||||
|
|
||||||
|
// Mock fs module
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
jest.mock('fs');
|
||||||
|
jest.mock('path');
|
||||||
|
|
||||||
|
describe('getProjectDisplayName', () => {
|
||||||
|
const originalAliases = {
|
||||||
|
'FT': '外放构建',
|
||||||
|
'FT_DEV': '开发构建',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock the aliases file
|
||||||
|
path.join.mockReturnValue('/mock/lib/project-aliases.json');
|
||||||
|
fs.readFileSync.mockReturnValue(JSON.stringify(originalAliases));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return display name for aliased project', () => {
|
||||||
|
expect(getProjectDisplayName('FT')).toBe('外放构建');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return directory name when no alias exists', () => {
|
||||||
|
expect(getProjectDisplayName('UNKNOWN')).toBe('UNKNOWN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return directory name when aliases file is empty', () => {
|
||||||
|
fs.readFileSync.mockReturnValue('{}');
|
||||||
|
expect(getProjectDisplayName('FT')).toBe('FT');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return directory name when aliases file cannot be read', () => {
|
||||||
|
fs.readFileSync.mockImplementation(() => {
|
||||||
|
throw new Error('File not found');
|
||||||
|
});
|
||||||
|
expect(getProjectDisplayName('FT')).toBe('FT');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
import { formatVersion, formatFileSize, formatDateTime, getProjectDisplayName } from '../utils';
|
import { formatVersion, formatFileSize, formatDateTime } from '../utils';
|
||||||
|
|
||||||
// Mock fs module
|
|
||||||
const fs = require('fs');
|
|
||||||
jest.mock('fs', () => ({
|
|
||||||
...jest.requireActual('fs'),
|
|
||||||
readFileSync: jest.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('formatVersion', () => {
|
describe('formatVersion', () => {
|
||||||
test('应该将下划线替换为点号', () => {
|
test('应该将下划线替换为点号', () => {
|
||||||
@@ -70,39 +63,3 @@ describe('formatDateTime', () => {
|
|||||||
expect(formatDateTime(date)).toBe('2024-01-05 09:05');
|
expect(formatDateTime(date)).toBe('2024-01-05 09:05');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getProjectDisplayName', () => {
|
|
||||||
const originalAliases = {
|
|
||||||
'FT': '外放构建',
|
|
||||||
'FT_DEV': '开发构建',
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
// Mock the aliases file
|
|
||||||
fs.readFileSync.mockReturnValue(JSON.stringify(originalAliases));
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return display name for aliased project', () => {
|
|
||||||
expect(getProjectDisplayName('FT')).toBe('外放构建');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return directory name when no alias exists', () => {
|
|
||||||
expect(getProjectDisplayName('UNKNOWN')).toBe('UNKNOWN');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return directory name when aliases file is empty', () => {
|
|
||||||
fs.readFileSync.mockReturnValue('{}');
|
|
||||||
expect(getProjectDisplayName('FT')).toBe('FT');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return directory name when aliases file cannot be read', () => {
|
|
||||||
fs.readFileSync.mockImplementation(() => {
|
|
||||||
throw new Error('File not found');
|
|
||||||
});
|
|
||||||
expect(getProjectDisplayName('FT')).toBe('FT');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
19
lib/project-utils.ts
Normal file
19
lib/project-utils.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// lib/project-utils.ts
|
||||||
|
// Server-only utility functions
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目显示名称(从别名配置)
|
||||||
|
*/
|
||||||
|
export function getProjectDisplayName(projectName: string): string {
|
||||||
|
try {
|
||||||
|
const aliasesPath = path.join(process.cwd(), 'lib', 'project-aliases.json');
|
||||||
|
const aliasesContent = fs.readFileSync(aliasesPath, 'utf-8');
|
||||||
|
const aliases = JSON.parse(aliasesContent);
|
||||||
|
return aliases[projectName] || projectName;
|
||||||
|
} catch {
|
||||||
|
// If file doesn't exist or is invalid, return original name
|
||||||
|
return projectName;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
lib/utils.ts
17
lib/utils.ts
@@ -1,7 +1,5 @@
|
|||||||
// lib/utils.ts
|
// lib/utils.ts
|
||||||
// Client-safe utility functions
|
// Client-safe utility functions
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化版本号显示 (0_42 -> 0.42)
|
* 格式化版本号显示 (0_42 -> 0.42)
|
||||||
@@ -31,18 +29,3 @@ export function formatDateTime(date: Date): string {
|
|||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取项目显示名称(从别名配置)
|
|
||||||
*/
|
|
||||||
export function getProjectDisplayName(projectName: string): string {
|
|
||||||
try {
|
|
||||||
const aliasesPath = path.join(process.cwd(), 'lib', 'project-aliases.json');
|
|
||||||
const aliasesContent = fs.readFileSync(aliasesPath, 'utf-8');
|
|
||||||
const aliases = JSON.parse(aliasesContent);
|
|
||||||
return aliases[projectName] || projectName;
|
|
||||||
} catch {
|
|
||||||
// If file doesn't exist or is invalid, return original name
|
|
||||||
return projectName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user