@jlcpcb/core 0.1.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.
- package/CHANGELOG.md +10 -0
- package/README.md +474 -0
- package/package.json +48 -0
- package/src/api/easyeda-community.ts +259 -0
- package/src/api/easyeda.ts +153 -0
- package/src/api/index.ts +7 -0
- package/src/api/jlc.ts +185 -0
- package/src/constants/design-rules.ts +119 -0
- package/src/constants/footprints.ts +68 -0
- package/src/constants/index.ts +7 -0
- package/src/constants/kicad.ts +147 -0
- package/src/converter/category-router.ts +638 -0
- package/src/converter/footprint-mapper.ts +236 -0
- package/src/converter/footprint.ts +949 -0
- package/src/converter/global-lib-table.ts +394 -0
- package/src/converter/index.ts +46 -0
- package/src/converter/lib-table.ts +181 -0
- package/src/converter/svg-arc.ts +179 -0
- package/src/converter/symbol-templates.ts +214 -0
- package/src/converter/symbol.ts +1682 -0
- package/src/converter/value-normalizer.ts +262 -0
- package/src/index.ts +25 -0
- package/src/parsers/easyeda-shapes.ts +628 -0
- package/src/parsers/http-client.ts +96 -0
- package/src/parsers/index.ts +38 -0
- package/src/parsers/utils.ts +29 -0
- package/src/services/component-service.ts +100 -0
- package/src/services/fix-service.ts +50 -0
- package/src/services/index.ts +9 -0
- package/src/services/library-service.ts +696 -0
- package/src/types/component.ts +61 -0
- package/src/types/easyeda-community.ts +78 -0
- package/src/types/easyeda.ts +356 -0
- package/src/types/index.ts +12 -0
- package/src/types/jlc.ts +84 -0
- package/src/types/kicad.ts +136 -0
- package/src/types/mcp.ts +77 -0
- package/src/types/project.ts +60 -0
- package/src/utils/conversion.ts +104 -0
- package/src/utils/file-system.ts +143 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +96 -0
- package/src/utils/validation.ts +110 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KiCad-specific types for schematic and PCB manipulation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface PinDefinition {
|
|
6
|
+
number: string;
|
|
7
|
+
name: string;
|
|
8
|
+
electricalType: 'input' | 'output' | 'bidirectional' | 'power_in' | 'power_out' | 'passive' | 'unspecified' | 'open_collector' | 'open_emitter' | 'no_connect';
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
rotation: number;
|
|
12
|
+
length: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PadDefinition {
|
|
16
|
+
number: string;
|
|
17
|
+
type: 'thru_hole' | 'smd' | 'connect' | 'np_thru_hole';
|
|
18
|
+
shape: 'circle' | 'rect' | 'oval' | 'trapezoid' | 'roundrect' | 'custom';
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
drill?: number;
|
|
24
|
+
layers: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface BoundingBox {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface KiCadSymbol {
|
|
35
|
+
name: string;
|
|
36
|
+
library: string;
|
|
37
|
+
pins: PinDefinition[];
|
|
38
|
+
properties: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface KiCadFootprint {
|
|
42
|
+
name: string;
|
|
43
|
+
library: string;
|
|
44
|
+
pads: PadDefinition[];
|
|
45
|
+
layers: string[];
|
|
46
|
+
courtyard: BoundingBox;
|
|
47
|
+
properties: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SchematicComponent {
|
|
51
|
+
reference: string;
|
|
52
|
+
value: string;
|
|
53
|
+
symbol: string;
|
|
54
|
+
footprint: string;
|
|
55
|
+
position: { x: number; y: number };
|
|
56
|
+
rotation: number;
|
|
57
|
+
properties: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface Wire {
|
|
61
|
+
startX: number;
|
|
62
|
+
startY: number;
|
|
63
|
+
endX: number;
|
|
64
|
+
endY: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface NetLabel {
|
|
68
|
+
name: string;
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
rotation: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface SchematicSheet {
|
|
75
|
+
name: string;
|
|
76
|
+
filename: string;
|
|
77
|
+
position: { x: number; y: number };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface KiCadSchematic {
|
|
81
|
+
version: string;
|
|
82
|
+
components: SchematicComponent[];
|
|
83
|
+
wires: Wire[];
|
|
84
|
+
labels: NetLabel[];
|
|
85
|
+
sheets: SchematicSheet[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface KiCadPCB {
|
|
89
|
+
version: string;
|
|
90
|
+
layers: PCBLayer[];
|
|
91
|
+
components: PCBComponent[];
|
|
92
|
+
traces: Trace[];
|
|
93
|
+
vias: Via[];
|
|
94
|
+
zones: Zone[];
|
|
95
|
+
boardOutline: BoardOutline;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface PCBLayer {
|
|
99
|
+
number: number;
|
|
100
|
+
name: string;
|
|
101
|
+
type: 'signal' | 'power' | 'mixed' | 'user';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface PCBComponent {
|
|
105
|
+
reference: string;
|
|
106
|
+
footprint: string;
|
|
107
|
+
position: { x: number; y: number };
|
|
108
|
+
rotation: number;
|
|
109
|
+
layer: 'F.Cu' | 'B.Cu';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface Trace {
|
|
113
|
+
net: string;
|
|
114
|
+
width: number;
|
|
115
|
+
layer: string;
|
|
116
|
+
points: { x: number; y: number }[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface Via {
|
|
120
|
+
net: string;
|
|
121
|
+
position: { x: number; y: number };
|
|
122
|
+
size: number;
|
|
123
|
+
drill: number;
|
|
124
|
+
layers: [string, string];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface Zone {
|
|
128
|
+
net: string;
|
|
129
|
+
layer: string;
|
|
130
|
+
priority: number;
|
|
131
|
+
outline: { x: number; y: number }[];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface BoardOutline {
|
|
135
|
+
points: { x: number; y: number }[];
|
|
136
|
+
}
|
package/src/types/mcp.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP protocol types for AI-EDA tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface MCPToolResult {
|
|
6
|
+
content: MCPContent[];
|
|
7
|
+
isError?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface MCPContent {
|
|
11
|
+
type: 'text' | 'image' | 'resource';
|
|
12
|
+
text?: string;
|
|
13
|
+
data?: string;
|
|
14
|
+
mimeType?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MCPToolDefinition {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: 'object';
|
|
22
|
+
properties: Record<string, MCPPropertySchema>;
|
|
23
|
+
required?: string[];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MCPPropertySchema {
|
|
28
|
+
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
29
|
+
description?: string;
|
|
30
|
+
enum?: string[];
|
|
31
|
+
items?: MCPPropertySchema;
|
|
32
|
+
default?: unknown;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MCPServerInfo {
|
|
36
|
+
name: string;
|
|
37
|
+
version: string;
|
|
38
|
+
capabilities: {
|
|
39
|
+
tools?: boolean;
|
|
40
|
+
resources?: boolean;
|
|
41
|
+
prompts?: boolean;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Tool-specific types
|
|
46
|
+
export interface ComponentSearchParams {
|
|
47
|
+
query: string;
|
|
48
|
+
category?: string;
|
|
49
|
+
inStock?: boolean;
|
|
50
|
+
limit?: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface LibraryFetchParams {
|
|
54
|
+
lcscPartNumber: string;
|
|
55
|
+
outputDir?: string;
|
|
56
|
+
include3D?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface DatasheetDownloadParams {
|
|
60
|
+
lcscPartNumber: string;
|
|
61
|
+
outputPath?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface SchematicParams {
|
|
65
|
+
projectPath: string;
|
|
66
|
+
sheetName?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface PCBParams {
|
|
70
|
+
projectPath: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ExportParams {
|
|
74
|
+
projectPath: string;
|
|
75
|
+
format: 'jlcpcb' | 'pcbway' | 'oshpark' | 'generic';
|
|
76
|
+
outputDir?: string;
|
|
77
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project structure and configuration types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ComponentSelection } from './component.js';
|
|
6
|
+
|
|
7
|
+
export interface EDAProject {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
created: string;
|
|
11
|
+
modified: string;
|
|
12
|
+
constraints: DesignConstraints;
|
|
13
|
+
components: ComponentSelection[];
|
|
14
|
+
status: ProjectStatus;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface DesignConstraints {
|
|
18
|
+
boardSize?: { width: number; height: number; unit: 'mm' | 'inch' };
|
|
19
|
+
layers: number;
|
|
20
|
+
powerSource: PowerSourceSpec;
|
|
21
|
+
interfaces: InterfaceSpec[];
|
|
22
|
+
environment?: EnvironmentSpec;
|
|
23
|
+
manufacturingClass?: number; // IPC class 1/2/3
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PowerSourceSpec {
|
|
27
|
+
type: 'usb' | 'battery' | 'dc_jack' | 'poe' | 'other';
|
|
28
|
+
voltage: { min: number; max: number };
|
|
29
|
+
current?: number;
|
|
30
|
+
details?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface InterfaceSpec {
|
|
34
|
+
type: 'usb' | 'uart' | 'spi' | 'i2c' | 'ethernet' | 'wifi' | 'bluetooth' | 'can' | 'rs485' | 'gpio' | 'adc' | 'dac' | 'pwm' | 'other';
|
|
35
|
+
count?: number;
|
|
36
|
+
details?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface EnvironmentSpec {
|
|
40
|
+
tempMin: number;
|
|
41
|
+
tempMax: number;
|
|
42
|
+
indoor: boolean;
|
|
43
|
+
certifications?: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ProjectStatus {
|
|
47
|
+
phase: 'specification' | 'component_selection' | 'schematic' | 'pcb_layout' | 'validation' | 'manufacturing';
|
|
48
|
+
schematicComplete: boolean;
|
|
49
|
+
pcbComplete: boolean;
|
|
50
|
+
validated: boolean;
|
|
51
|
+
exported: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ProjectConfig {
|
|
55
|
+
kicadProjectDir: string;
|
|
56
|
+
librariesDir: string;
|
|
57
|
+
datasheetsDir: string;
|
|
58
|
+
productionDir: string;
|
|
59
|
+
docsDir: string;
|
|
60
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit conversion utilities for EDA operations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// EasyEDA uses 10mil units (0.254mm per unit)
|
|
6
|
+
export const EASYEDA_TO_MM = 0.254;
|
|
7
|
+
export const MM_TO_EASYEDA = 1 / EASYEDA_TO_MM;
|
|
8
|
+
|
|
9
|
+
// KiCad uses mm as default
|
|
10
|
+
export const MM_TO_MIL = 39.3701;
|
|
11
|
+
export const MIL_TO_MM = 0.0254;
|
|
12
|
+
export const MM_TO_INCH = 0.0393701;
|
|
13
|
+
export const INCH_TO_MM = 25.4;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Convert EasyEDA units to millimeters
|
|
17
|
+
*/
|
|
18
|
+
export function easyedaToMm(value: number): number {
|
|
19
|
+
return value * EASYEDA_TO_MM;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convert millimeters to EasyEDA units
|
|
24
|
+
*/
|
|
25
|
+
export function mmToEasyeda(value: number): number {
|
|
26
|
+
return value * MM_TO_EASYEDA;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Convert mils to millimeters
|
|
31
|
+
*/
|
|
32
|
+
export function milToMm(value: number): number {
|
|
33
|
+
return value * MIL_TO_MM;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convert millimeters to mils
|
|
38
|
+
*/
|
|
39
|
+
export function mmToMil(value: number): number {
|
|
40
|
+
return value * MM_TO_MIL;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert inches to millimeters
|
|
45
|
+
*/
|
|
46
|
+
export function inchToMm(value: number): number {
|
|
47
|
+
return value * INCH_TO_MM;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Convert millimeters to inches
|
|
52
|
+
*/
|
|
53
|
+
export function mmToInch(value: number): number {
|
|
54
|
+
return value * MM_TO_INCH;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Round to a specific number of decimal places
|
|
59
|
+
*/
|
|
60
|
+
export function roundTo(value: number, decimals: number = 4): number {
|
|
61
|
+
const multiplier = Math.pow(10, decimals);
|
|
62
|
+
return Math.round(value * multiplier) / multiplier;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Convert angle from degrees to radians
|
|
67
|
+
*/
|
|
68
|
+
export function degToRad(degrees: number): number {
|
|
69
|
+
return (degrees * Math.PI) / 180;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Convert angle from radians to degrees
|
|
74
|
+
*/
|
|
75
|
+
export function radToDeg(radians: number): number {
|
|
76
|
+
return (radians * 180) / Math.PI;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Normalize angle to 0-360 range
|
|
81
|
+
*/
|
|
82
|
+
export function normalizeAngle(angle: number): number {
|
|
83
|
+
return ((angle % 360) + 360) % 360;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Calculate trace width for given current and temperature rise
|
|
88
|
+
* Using IPC-2221 standard for external layers
|
|
89
|
+
* @param currentAmps - Current in amperes
|
|
90
|
+
* @param tempRiseC - Temperature rise in Celsius (default 10)
|
|
91
|
+
* @param copperOz - Copper weight in oz/ft² (default 1)
|
|
92
|
+
* @returns Trace width in mm
|
|
93
|
+
*/
|
|
94
|
+
export function calculateTraceWidth(
|
|
95
|
+
currentAmps: number,
|
|
96
|
+
tempRiseC: number = 10,
|
|
97
|
+
copperOz: number = 1
|
|
98
|
+
): number {
|
|
99
|
+
// IPC-2221 formula for external layers
|
|
100
|
+
const area = Math.pow(currentAmps / (0.048 * Math.pow(tempRiseC, 0.44)), 1 / 0.725);
|
|
101
|
+
const thickness = copperOz * 0.035; // mm
|
|
102
|
+
const width = area / (thickness * 1000); // convert from mils² to mm
|
|
103
|
+
return roundTo(width, 3);
|
|
104
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system operation helpers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readdir, readFile, writeFile, mkdir, access, stat } from 'fs/promises';
|
|
6
|
+
import { join, dirname, extname, basename } from 'path';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if a path exists
|
|
10
|
+
*/
|
|
11
|
+
export async function pathExists(path: string): Promise<boolean> {
|
|
12
|
+
try {
|
|
13
|
+
await access(path);
|
|
14
|
+
return true;
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a path is a directory
|
|
22
|
+
*/
|
|
23
|
+
export async function isDirectory(path: string): Promise<boolean> {
|
|
24
|
+
try {
|
|
25
|
+
const stats = await stat(path);
|
|
26
|
+
return stats.isDirectory();
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check if a path is a file
|
|
34
|
+
*/
|
|
35
|
+
export async function isFile(path: string): Promise<boolean> {
|
|
36
|
+
try {
|
|
37
|
+
const stats = await stat(path);
|
|
38
|
+
return stats.isFile();
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Ensure a directory exists, creating it if necessary
|
|
46
|
+
*/
|
|
47
|
+
export async function ensureDir(path: string): Promise<void> {
|
|
48
|
+
await mkdir(path, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Ensure the parent directory of a file exists
|
|
53
|
+
*/
|
|
54
|
+
export async function ensureParentDir(filePath: string): Promise<void> {
|
|
55
|
+
const parentDir = dirname(filePath);
|
|
56
|
+
await ensureDir(parentDir);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read a JSON file and parse it
|
|
61
|
+
*/
|
|
62
|
+
export async function readJson<T = unknown>(path: string): Promise<T> {
|
|
63
|
+
const content = await readFile(path, 'utf-8');
|
|
64
|
+
return JSON.parse(content) as T;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Write an object to a JSON file
|
|
69
|
+
*/
|
|
70
|
+
export async function writeJson(path: string, data: unknown, pretty: boolean = true): Promise<void> {
|
|
71
|
+
await ensureParentDir(path);
|
|
72
|
+
const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
73
|
+
await writeFile(path, content, 'utf-8');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Read a text file
|
|
78
|
+
*/
|
|
79
|
+
export async function readText(path: string): Promise<string> {
|
|
80
|
+
return readFile(path, 'utf-8');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Write text to a file
|
|
85
|
+
*/
|
|
86
|
+
export async function writeText(path: string, content: string): Promise<void> {
|
|
87
|
+
await ensureParentDir(path);
|
|
88
|
+
await writeFile(path, content, 'utf-8');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Write binary data to a file
|
|
93
|
+
*/
|
|
94
|
+
export async function writeBinary(path: string, data: Buffer | Uint8Array): Promise<void> {
|
|
95
|
+
await ensureParentDir(path);
|
|
96
|
+
await writeFile(path, data);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* List files in a directory with optional filtering
|
|
101
|
+
*/
|
|
102
|
+
export async function listFiles(
|
|
103
|
+
dir: string,
|
|
104
|
+
options: { extensions?: string[]; recursive?: boolean } = {}
|
|
105
|
+
): Promise<string[]> {
|
|
106
|
+
const { extensions, recursive = false } = options;
|
|
107
|
+
const files: string[] = [];
|
|
108
|
+
|
|
109
|
+
async function scanDir(currentDir: string): Promise<void> {
|
|
110
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
111
|
+
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const fullPath = join(currentDir, entry.name);
|
|
114
|
+
|
|
115
|
+
if (entry.isDirectory() && recursive) {
|
|
116
|
+
await scanDir(fullPath);
|
|
117
|
+
} else if (entry.isFile()) {
|
|
118
|
+
if (!extensions || extensions.includes(extname(entry.name))) {
|
|
119
|
+
files.push(fullPath);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await scanDir(dir);
|
|
126
|
+
return files;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get the filename without extension
|
|
131
|
+
*/
|
|
132
|
+
export function getBaseName(filePath: string): string {
|
|
133
|
+
return basename(filePath, extname(filePath));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Copy a file
|
|
138
|
+
*/
|
|
139
|
+
export async function copyFile(src: string, dest: string): Promise<void> {
|
|
140
|
+
await ensureParentDir(dest);
|
|
141
|
+
const content = await readFile(src);
|
|
142
|
+
await writeFile(dest, content);
|
|
143
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consistent logging utility for AI-EDA packages
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
6
|
+
|
|
7
|
+
export interface LoggerOptions {
|
|
8
|
+
level: LogLevel;
|
|
9
|
+
prefix?: string;
|
|
10
|
+
timestamps?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
14
|
+
debug: 0,
|
|
15
|
+
info: 1,
|
|
16
|
+
warn: 2,
|
|
17
|
+
error: 3,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class Logger {
|
|
21
|
+
private level: LogLevel;
|
|
22
|
+
private prefix: string;
|
|
23
|
+
private timestamps: boolean;
|
|
24
|
+
|
|
25
|
+
constructor(options: Partial<LoggerOptions> = {}) {
|
|
26
|
+
this.level = options.level ?? 'info';
|
|
27
|
+
this.prefix = options.prefix ?? '';
|
|
28
|
+
this.timestamps = options.timestamps ?? false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private shouldLog(level: LogLevel): boolean {
|
|
32
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[this.level];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private formatMessage(level: LogLevel, message: string): string {
|
|
36
|
+
const parts: string[] = [];
|
|
37
|
+
|
|
38
|
+
if (this.timestamps) {
|
|
39
|
+
parts.push(`[${new Date().toISOString()}]`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
parts.push(`[${level.toUpperCase()}]`);
|
|
43
|
+
|
|
44
|
+
if (this.prefix) {
|
|
45
|
+
parts.push(`[${this.prefix}]`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
parts.push(message);
|
|
49
|
+
|
|
50
|
+
return parts.join(' ');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
debug(message: string, ...args: unknown[]): void {
|
|
54
|
+
if (this.shouldLog('debug')) {
|
|
55
|
+
console.error(this.formatMessage('debug', message), ...args);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
info(message: string, ...args: unknown[]): void {
|
|
60
|
+
if (this.shouldLog('info')) {
|
|
61
|
+
console.error(this.formatMessage('info', message), ...args);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
warn(message: string, ...args: unknown[]): void {
|
|
66
|
+
if (this.shouldLog('warn')) {
|
|
67
|
+
console.error(this.formatMessage('warn', message), ...args);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
error(message: string, ...args: unknown[]): void {
|
|
72
|
+
if (this.shouldLog('error')) {
|
|
73
|
+
console.error(this.formatMessage('error', message), ...args);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setLevel(level: LogLevel): void {
|
|
78
|
+
this.level = level;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
child(prefix: string): Logger {
|
|
82
|
+
return new Logger({
|
|
83
|
+
level: this.level,
|
|
84
|
+
prefix: this.prefix ? `${this.prefix}:${prefix}` : prefix,
|
|
85
|
+
timestamps: this.timestamps,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Default logger instance
|
|
91
|
+
export const logger = new Logger({ prefix: 'ai-eda' });
|
|
92
|
+
|
|
93
|
+
// Create package-specific loggers
|
|
94
|
+
export function createLogger(packageName: string): Logger {
|
|
95
|
+
return logger.child(packageName);
|
|
96
|
+
}
|