@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,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Library Table Manager
|
|
3
|
+
* Registers JLC libraries in KiCad's global sym-lib-table and fp-lib-table
|
|
4
|
+
* Works cross-platform: macOS, Windows, Linux
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { homedir, platform } from 'os';
|
|
11
|
+
import {
|
|
12
|
+
getAllCategories,
|
|
13
|
+
getLibraryFilename,
|
|
14
|
+
getFootprintDirName,
|
|
15
|
+
get3DModelsDirName,
|
|
16
|
+
type LibraryCategory,
|
|
17
|
+
} from './category-router.js';
|
|
18
|
+
import { libraryExistsInTable, addLibraryToTable } from './lib-table.js';
|
|
19
|
+
|
|
20
|
+
// KiCad versions to check (newest first)
|
|
21
|
+
const KICAD_VERSIONS = ['9.0', '8.0'];
|
|
22
|
+
|
|
23
|
+
// Library prefix - matches category-router.ts
|
|
24
|
+
const LIBRARY_PREFIX = 'JLC-MCP';
|
|
25
|
+
|
|
26
|
+
// 3rd party library namespace (subfolder under 3rdparty/)
|
|
27
|
+
const LIBRARY_NAMESPACE = 'jlc_mcp';
|
|
28
|
+
|
|
29
|
+
// KiCad environment variable for 3rd party libraries
|
|
30
|
+
// This resolves to ~/Documents/KiCad/{version}/3rdparty/ on all platforms
|
|
31
|
+
const KICAD_3RD_PARTY_VAR = '${KICAD9_3RD_PARTY}';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Detect KiCad major version from existing user directories
|
|
35
|
+
*/
|
|
36
|
+
function detectKicadVersion(): string {
|
|
37
|
+
const home = homedir();
|
|
38
|
+
const baseDir = join(home, 'Documents', 'KiCad');
|
|
39
|
+
|
|
40
|
+
for (const version of KICAD_VERSIONS) {
|
|
41
|
+
if (existsSync(join(baseDir, version))) {
|
|
42
|
+
return version;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return '9.0'; // Default
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get KiCad global config directory (platform-specific)
|
|
50
|
+
* This is where sym-lib-table and fp-lib-table are stored
|
|
51
|
+
*/
|
|
52
|
+
function getKicadConfigDir(version: string): string {
|
|
53
|
+
const home = homedir();
|
|
54
|
+
const plat = platform();
|
|
55
|
+
|
|
56
|
+
if (plat === 'darwin') {
|
|
57
|
+
return join(home, 'Library', 'Preferences', 'kicad', version);
|
|
58
|
+
} else if (plat === 'win32') {
|
|
59
|
+
return join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), 'kicad', version);
|
|
60
|
+
} else {
|
|
61
|
+
// Linux and others
|
|
62
|
+
return join(home, '.config', 'kicad', version);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get KiCad 3rd party library base directory (where libraries are stored)
|
|
68
|
+
* Platform-specific paths matching where ${KICAD9_3RD_PARTY} resolves:
|
|
69
|
+
* - macOS/Windows: ~/Documents/KiCad/{version}/3rdparty/
|
|
70
|
+
* - Linux: ~/.local/share/kicad/{version}/3rdparty/
|
|
71
|
+
*/
|
|
72
|
+
function get3rdPartyBaseDir(version: string): string {
|
|
73
|
+
const home = homedir();
|
|
74
|
+
const plat = platform();
|
|
75
|
+
|
|
76
|
+
if (plat === 'linux') {
|
|
77
|
+
return join(home, '.local', 'share', 'kicad', version, '3rdparty', LIBRARY_NAMESPACE);
|
|
78
|
+
}
|
|
79
|
+
// macOS and Windows use Documents/KiCad
|
|
80
|
+
return join(home, 'Documents', 'KiCad', version, '3rdparty', LIBRARY_NAMESPACE);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get absolute path to a JLC-MCP symbol library file (for file operations)
|
|
85
|
+
*/
|
|
86
|
+
function getSymbolLibPath(category: LibraryCategory, version: string): string {
|
|
87
|
+
return join(get3rdPartyBaseDir(version), 'symbols', getLibraryFilename(category));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get portable URI for a JLC-MCP symbol library (for table entries)
|
|
92
|
+
* Uses ${KICAD9_3RD_PARTY} variable for cross-system compatibility
|
|
93
|
+
*/
|
|
94
|
+
function getSymbolLibUri(category: LibraryCategory): string {
|
|
95
|
+
return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/symbols/${getLibraryFilename(category)}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get absolute path to JLC-MCP footprint library directory (for file operations)
|
|
100
|
+
*/
|
|
101
|
+
function getFootprintLibPath(version: string): string {
|
|
102
|
+
return join(get3rdPartyBaseDir(version), 'footprints', getFootprintDirName());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get portable URI for JLC-MCP footprint library (for table entries)
|
|
107
|
+
* Uses ${KICAD9_3RD_PARTY} variable for cross-system compatibility
|
|
108
|
+
*/
|
|
109
|
+
function getFootprintLibUri(): string {
|
|
110
|
+
return `${KICAD_3RD_PARTY_VAR}/${LIBRARY_NAMESPACE}/footprints/${getFootprintDirName()}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Generate standard KiCad symbol library header
|
|
115
|
+
*/
|
|
116
|
+
function generateEmptySymbolLibrary(): string {
|
|
117
|
+
return `(kicad_symbol_lib
|
|
118
|
+
\t(version 20241209)
|
|
119
|
+
\t(generator "jlc-mcp")
|
|
120
|
+
\t(generator_version "9.0")
|
|
121
|
+
)\n`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Library description for table entries
|
|
125
|
+
const LIBRARY_DESCRIPTION = 'Autogenerated by JLC-MCP';
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Generate new sym-lib-table content with all JLC-MCP libraries
|
|
129
|
+
* Uses ${KICAD9_3RD_PARTY} variable for portable paths
|
|
130
|
+
*/
|
|
131
|
+
function generateSymLibTable(): string {
|
|
132
|
+
const categories = getAllCategories();
|
|
133
|
+
let content = '(sym_lib_table\n (version 7)\n';
|
|
134
|
+
|
|
135
|
+
for (const category of categories) {
|
|
136
|
+
const name = `${LIBRARY_PREFIX}-${category}`;
|
|
137
|
+
const uri = getSymbolLibUri(category);
|
|
138
|
+
content += ` (lib (name "${name}")(type "KiCad")(uri "${uri}")(options "")(descr "${LIBRARY_DESCRIPTION}"))\n`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
content += ')\n';
|
|
142
|
+
return content;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Generate new fp-lib-table content with JLC-MCP footprint library
|
|
147
|
+
* Uses ${KICAD9_3RD_PARTY} variable for portable paths
|
|
148
|
+
*/
|
|
149
|
+
function generateFpLibTable(): string {
|
|
150
|
+
const name = LIBRARY_PREFIX;
|
|
151
|
+
const uri = getFootprintLibUri();
|
|
152
|
+
|
|
153
|
+
return `(fp_lib_table
|
|
154
|
+
(version 7)
|
|
155
|
+
(lib (name "${name}")(type "KiCad")(uri "${uri}")(options "")(descr "${LIBRARY_DESCRIPTION}"))
|
|
156
|
+
)
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface TableUpdateResult {
|
|
161
|
+
path: string;
|
|
162
|
+
created: boolean;
|
|
163
|
+
modified: boolean;
|
|
164
|
+
entriesAdded: number;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Ensure sym-lib-table has all JLC-MCP symbol libraries registered
|
|
169
|
+
*/
|
|
170
|
+
async function ensureGlobalSymLibTable(version: string): Promise<TableUpdateResult> {
|
|
171
|
+
const configDir = getKicadConfigDir(version);
|
|
172
|
+
const tablePath = join(configDir, 'sym-lib-table');
|
|
173
|
+
const categories = getAllCategories();
|
|
174
|
+
|
|
175
|
+
// Ensure config directory exists
|
|
176
|
+
await mkdir(configDir, { recursive: true });
|
|
177
|
+
|
|
178
|
+
if (!existsSync(tablePath)) {
|
|
179
|
+
// Create new table with all libraries
|
|
180
|
+
const content = generateSymLibTable();
|
|
181
|
+
await writeFile(tablePath, content, 'utf-8');
|
|
182
|
+
return {
|
|
183
|
+
path: tablePath,
|
|
184
|
+
created: true,
|
|
185
|
+
modified: false,
|
|
186
|
+
entriesAdded: categories.length,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Read existing table and add missing libraries
|
|
191
|
+
let content = await readFile(tablePath, 'utf-8');
|
|
192
|
+
let entriesAdded = 0;
|
|
193
|
+
|
|
194
|
+
for (const category of categories) {
|
|
195
|
+
const name = `${LIBRARY_PREFIX}-${category}`;
|
|
196
|
+
|
|
197
|
+
if (!libraryExistsInTable(content, name)) {
|
|
198
|
+
const uri = getSymbolLibUri(category);
|
|
199
|
+
content = addLibraryToTable(content, name, uri, 'sym', LIBRARY_DESCRIPTION);
|
|
200
|
+
|
|
201
|
+
// Validate entry was added correctly
|
|
202
|
+
if (!libraryExistsInTable(content, name)) {
|
|
203
|
+
throw new Error(`Failed to add symbol library ${name} to table`);
|
|
204
|
+
}
|
|
205
|
+
entriesAdded++;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (entriesAdded > 0) {
|
|
210
|
+
await writeFile(tablePath, content, 'utf-8');
|
|
211
|
+
return {
|
|
212
|
+
path: tablePath,
|
|
213
|
+
created: false,
|
|
214
|
+
modified: true,
|
|
215
|
+
entriesAdded,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
path: tablePath,
|
|
221
|
+
created: false,
|
|
222
|
+
modified: false,
|
|
223
|
+
entriesAdded: 0,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Ensure fp-lib-table has JLC-MCP footprint library registered
|
|
229
|
+
*/
|
|
230
|
+
async function ensureGlobalFpLibTable(version: string): Promise<TableUpdateResult> {
|
|
231
|
+
const configDir = getKicadConfigDir(version);
|
|
232
|
+
const tablePath = join(configDir, 'fp-lib-table');
|
|
233
|
+
const name = LIBRARY_PREFIX;
|
|
234
|
+
|
|
235
|
+
// Ensure config directory exists
|
|
236
|
+
await mkdir(configDir, { recursive: true });
|
|
237
|
+
|
|
238
|
+
if (!existsSync(tablePath)) {
|
|
239
|
+
// Create new table
|
|
240
|
+
const content = generateFpLibTable();
|
|
241
|
+
await writeFile(tablePath, content, 'utf-8');
|
|
242
|
+
return {
|
|
243
|
+
path: tablePath,
|
|
244
|
+
created: true,
|
|
245
|
+
modified: false,
|
|
246
|
+
entriesAdded: 1,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Read existing table and add if missing
|
|
251
|
+
let content = await readFile(tablePath, 'utf-8');
|
|
252
|
+
|
|
253
|
+
if (!libraryExistsInTable(content, name)) {
|
|
254
|
+
const uri = getFootprintLibUri();
|
|
255
|
+
content = addLibraryToTable(content, name, uri, 'fp', LIBRARY_DESCRIPTION);
|
|
256
|
+
|
|
257
|
+
// Validate entry was added correctly
|
|
258
|
+
if (!libraryExistsInTable(content, name)) {
|
|
259
|
+
throw new Error(`Failed to add footprint library ${name} to table`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
await writeFile(tablePath, content, 'utf-8');
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
path: tablePath,
|
|
266
|
+
created: false,
|
|
267
|
+
modified: true,
|
|
268
|
+
entriesAdded: 1,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
path: tablePath,
|
|
274
|
+
created: false,
|
|
275
|
+
modified: false,
|
|
276
|
+
entriesAdded: 0,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Ensure library directories and empty stub files exist
|
|
282
|
+
* Creates structure under ~/Documents/KiCad/{version}/3rdparty/jlc_mcp/
|
|
283
|
+
*/
|
|
284
|
+
async function ensureLibraryStubs(version: string): Promise<{
|
|
285
|
+
symbolsCreated: string[];
|
|
286
|
+
directoriesCreated: string[];
|
|
287
|
+
}> {
|
|
288
|
+
const baseDir = get3rdPartyBaseDir(version);
|
|
289
|
+
const symbolsDir = join(baseDir, 'symbols');
|
|
290
|
+
const footprintsDir = join(baseDir, 'footprints', getFootprintDirName());
|
|
291
|
+
const models3dDir = join(baseDir, '3dmodels', get3DModelsDirName());
|
|
292
|
+
|
|
293
|
+
const directoriesCreated: string[] = [];
|
|
294
|
+
const symbolsCreated: string[] = [];
|
|
295
|
+
|
|
296
|
+
// Create directories
|
|
297
|
+
for (const dir of [symbolsDir, footprintsDir, models3dDir]) {
|
|
298
|
+
if (!existsSync(dir)) {
|
|
299
|
+
await mkdir(dir, { recursive: true });
|
|
300
|
+
directoriesCreated.push(dir);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Create empty symbol library files
|
|
305
|
+
const categories = getAllCategories();
|
|
306
|
+
const emptyContent = generateEmptySymbolLibrary();
|
|
307
|
+
|
|
308
|
+
for (const category of categories) {
|
|
309
|
+
const filePath = getSymbolLibPath(category, version);
|
|
310
|
+
if (!existsSync(filePath)) {
|
|
311
|
+
await writeFile(filePath, emptyContent, 'utf-8');
|
|
312
|
+
symbolsCreated.push(filePath);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return { symbolsCreated, directoriesCreated };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export interface GlobalRegistrationResult {
|
|
320
|
+
success: boolean;
|
|
321
|
+
version: string;
|
|
322
|
+
symLibTable: TableUpdateResult;
|
|
323
|
+
fpLibTable: TableUpdateResult;
|
|
324
|
+
libraryStubs: {
|
|
325
|
+
symbolsCreated: string[];
|
|
326
|
+
directoriesCreated: string[];
|
|
327
|
+
};
|
|
328
|
+
errors: string[];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Main entry point: Ensure JLC libraries are registered in KiCad global tables
|
|
333
|
+
* Call this on MCP server startup
|
|
334
|
+
*/
|
|
335
|
+
export async function ensureGlobalLibraryTables(): Promise<GlobalRegistrationResult> {
|
|
336
|
+
const errors: string[] = [];
|
|
337
|
+
const version = detectKicadVersion();
|
|
338
|
+
|
|
339
|
+
let symLibTable: TableUpdateResult = {
|
|
340
|
+
path: '',
|
|
341
|
+
created: false,
|
|
342
|
+
modified: false,
|
|
343
|
+
entriesAdded: 0,
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
let fpLibTable: TableUpdateResult = {
|
|
347
|
+
path: '',
|
|
348
|
+
created: false,
|
|
349
|
+
modified: false,
|
|
350
|
+
entriesAdded: 0,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
let libraryStubs = {
|
|
354
|
+
symbolsCreated: [] as string[],
|
|
355
|
+
directoriesCreated: [] as string[],
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// Step 1: Create library stubs (directories and empty files)
|
|
359
|
+
try {
|
|
360
|
+
libraryStubs = await ensureLibraryStubs(version);
|
|
361
|
+
} catch (error) {
|
|
362
|
+
const msg = `Failed to create library stubs: ${error instanceof Error ? error.message : String(error)}`;
|
|
363
|
+
errors.push(msg);
|
|
364
|
+
// Continue anyway - tables might still work if directories exist
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Step 2: Update global sym-lib-table
|
|
368
|
+
try {
|
|
369
|
+
symLibTable = await ensureGlobalSymLibTable(version);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
const msg = `Failed to update sym-lib-table: ${error instanceof Error ? error.message : String(error)}`;
|
|
372
|
+
errors.push(msg);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Step 3: Update global fp-lib-table
|
|
376
|
+
try {
|
|
377
|
+
fpLibTable = await ensureGlobalFpLibTable(version);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
const msg = `Failed to update fp-lib-table: ${error instanceof Error ? error.message : String(error)}`;
|
|
380
|
+
errors.push(msg);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Success if at least the tables were updated (even if stubs failed)
|
|
384
|
+
const success = symLibTable.path !== '' && fpLibTable.path !== '' && errors.length === 0;
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
success,
|
|
388
|
+
version,
|
|
389
|
+
symLibTable,
|
|
390
|
+
fpLibTable,
|
|
391
|
+
libraryStubs,
|
|
392
|
+
errors,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EasyEDA to KiCad converters
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { SymbolConverter, symbolConverter, type SymbolConversionOptions } from './symbol.js';
|
|
6
|
+
export { FootprintConverter, footprintConverter, type FootprintConversionOptions, type FootprintResult } from './footprint.js';
|
|
7
|
+
export {
|
|
8
|
+
ensureSymLibTable,
|
|
9
|
+
ensureFpLibTable,
|
|
10
|
+
getSymbolReference,
|
|
11
|
+
getFootprintReference,
|
|
12
|
+
libraryExistsInTable,
|
|
13
|
+
} from './lib-table.js';
|
|
14
|
+
export {
|
|
15
|
+
mapToKicadFootprint,
|
|
16
|
+
getKicadFootprintRef,
|
|
17
|
+
isStandardPassive,
|
|
18
|
+
type FootprintMapping,
|
|
19
|
+
} from './footprint-mapper.js';
|
|
20
|
+
export {
|
|
21
|
+
normalizeValue,
|
|
22
|
+
detectComponentType,
|
|
23
|
+
extractDisplayValue,
|
|
24
|
+
type ComponentType,
|
|
25
|
+
type NormalizedValue,
|
|
26
|
+
} from './value-normalizer.js';
|
|
27
|
+
export {
|
|
28
|
+
getLibraryCategory,
|
|
29
|
+
getLibraryFilename,
|
|
30
|
+
getFootprintDirName,
|
|
31
|
+
get3DModelsDirName,
|
|
32
|
+
getSymbolReference as getCategorySymbolReference,
|
|
33
|
+
getFootprintReference as getCategoryFootprintReference,
|
|
34
|
+
getAllCategories,
|
|
35
|
+
parseLibraryName,
|
|
36
|
+
type LibraryCategory,
|
|
37
|
+
} from './category-router.js';
|
|
38
|
+
export {
|
|
39
|
+
getSymbolTemplate,
|
|
40
|
+
hasFixedTemplate,
|
|
41
|
+
type SymbolTemplate,
|
|
42
|
+
} from './symbol-templates.js';
|
|
43
|
+
export {
|
|
44
|
+
ensureGlobalLibraryTables,
|
|
45
|
+
type GlobalRegistrationResult,
|
|
46
|
+
} from './global-lib-table.js';
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KiCad Library Table Management
|
|
3
|
+
* Handles sym-lib-table and fp-lib-table generation and updates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
|
|
10
|
+
// Library configuration - matches category-router.ts LIBRARY_PREFIX
|
|
11
|
+
const DEFAULT_LIBRARY_NAME = 'JLC-MCP';
|
|
12
|
+
const DEFAULT_LIBRARY_DESCRIPTION = 'JLC-MCP Component Library (LCSC/EasyEDA)';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate sym-lib-table content
|
|
16
|
+
*/
|
|
17
|
+
export function generateSymLibTable(
|
|
18
|
+
symbolLibPath: string,
|
|
19
|
+
libraryName: string = DEFAULT_LIBRARY_NAME,
|
|
20
|
+
description: string = DEFAULT_LIBRARY_DESCRIPTION
|
|
21
|
+
): string {
|
|
22
|
+
return `(sym_lib_table
|
|
23
|
+
(version 7)
|
|
24
|
+
(lib (name "${libraryName}")(type "KiCad")(uri "${symbolLibPath}")(options "")(descr "${description}"))
|
|
25
|
+
)
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generate fp-lib-table content
|
|
31
|
+
*/
|
|
32
|
+
export function generateFpLibTable(
|
|
33
|
+
footprintLibPath: string,
|
|
34
|
+
libraryName: string = DEFAULT_LIBRARY_NAME,
|
|
35
|
+
description: string = DEFAULT_LIBRARY_DESCRIPTION
|
|
36
|
+
): string {
|
|
37
|
+
return `(fp_lib_table
|
|
38
|
+
(version 7)
|
|
39
|
+
(lib (name "${libraryName}")(type "KiCad")(uri "${footprintLibPath}")(options "")(descr "${description}"))
|
|
40
|
+
)
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if a library entry exists in a lib-table file
|
|
46
|
+
*/
|
|
47
|
+
export function libraryExistsInTable(tableContent: string, libraryName: string): boolean {
|
|
48
|
+
const pattern = new RegExp(`\\(name\\s+"${libraryName}"\\)`, 'm');
|
|
49
|
+
return pattern.test(tableContent);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Add a library entry to an existing lib-table content
|
|
54
|
+
*/
|
|
55
|
+
export function addLibraryToTable(
|
|
56
|
+
tableContent: string,
|
|
57
|
+
libraryName: string,
|
|
58
|
+
libraryPath: string,
|
|
59
|
+
type: 'sym' | 'fp',
|
|
60
|
+
description: string = DEFAULT_LIBRARY_DESCRIPTION
|
|
61
|
+
): string {
|
|
62
|
+
// Check if library already exists
|
|
63
|
+
if (libraryExistsInTable(tableContent, libraryName)) {
|
|
64
|
+
return tableContent; // No change needed
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const trimmed = tableContent.trimEnd();
|
|
68
|
+
if (!trimmed.endsWith(')')) {
|
|
69
|
+
throw new Error('Invalid lib-table format: missing closing parenthesis');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Remove the last closing paren
|
|
73
|
+
const withoutClose = trimmed.slice(0, -1);
|
|
74
|
+
|
|
75
|
+
// Add new library entry
|
|
76
|
+
const newEntry = ` (lib (name "${libraryName}")(type "KiCad")(uri "${libraryPath}")(options "")(descr "${description}"))\n`;
|
|
77
|
+
|
|
78
|
+
return withoutClose + newEntry + ')\n';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Ensure sym-lib-table exists and contains specified library
|
|
83
|
+
* @param projectDir - Path to the KiCad project directory
|
|
84
|
+
* @param symbolLibPath - Path to the symbol library file (relative or absolute)
|
|
85
|
+
* @param libraryName - Library name (default: LCSC)
|
|
86
|
+
* @param description - Library description
|
|
87
|
+
* @returns true if table was created or modified, false if already correct
|
|
88
|
+
*/
|
|
89
|
+
export async function ensureSymLibTable(
|
|
90
|
+
projectDir: string,
|
|
91
|
+
symbolLibPath: string,
|
|
92
|
+
libraryName: string = DEFAULT_LIBRARY_NAME,
|
|
93
|
+
description: string = DEFAULT_LIBRARY_DESCRIPTION
|
|
94
|
+
): Promise<{ created: boolean; modified: boolean; path: string }> {
|
|
95
|
+
const tablePath = join(projectDir, 'sym-lib-table');
|
|
96
|
+
|
|
97
|
+
// Use ${KIPRJMOD} for project-relative paths
|
|
98
|
+
const relativePath = symbolLibPath.startsWith(projectDir)
|
|
99
|
+
? '${KIPRJMOD}' + symbolLibPath.slice(projectDir.length)
|
|
100
|
+
: symbolLibPath;
|
|
101
|
+
|
|
102
|
+
if (!existsSync(tablePath)) {
|
|
103
|
+
// Create new table
|
|
104
|
+
const content = generateSymLibTable(relativePath, libraryName, description);
|
|
105
|
+
await writeFile(tablePath, content, 'utf-8');
|
|
106
|
+
return { created: true, modified: false, path: tablePath };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Read existing table
|
|
110
|
+
const existingContent = await readFile(tablePath, 'utf-8');
|
|
111
|
+
|
|
112
|
+
if (libraryExistsInTable(existingContent, libraryName)) {
|
|
113
|
+
return { created: false, modified: false, path: tablePath };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Add library to existing table
|
|
117
|
+
const updatedContent = addLibraryToTable(existingContent, libraryName, relativePath, 'sym', description);
|
|
118
|
+
await writeFile(tablePath, updatedContent, 'utf-8');
|
|
119
|
+
return { created: false, modified: true, path: tablePath };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Ensure fp-lib-table exists and contains specified library
|
|
124
|
+
* @param projectDir - Path to the KiCad project directory
|
|
125
|
+
* @param footprintLibPath - Path to the footprint library directory (relative or absolute)
|
|
126
|
+
* @param libraryName - Library name (default: LCSC)
|
|
127
|
+
* @param description - Library description
|
|
128
|
+
* @returns true if table was created or modified, false if already correct
|
|
129
|
+
*/
|
|
130
|
+
export async function ensureFpLibTable(
|
|
131
|
+
projectDir: string,
|
|
132
|
+
footprintLibPath: string,
|
|
133
|
+
libraryName: string = DEFAULT_LIBRARY_NAME,
|
|
134
|
+
description: string = DEFAULT_LIBRARY_DESCRIPTION
|
|
135
|
+
): Promise<{ created: boolean; modified: boolean; path: string }> {
|
|
136
|
+
const tablePath = join(projectDir, 'fp-lib-table');
|
|
137
|
+
|
|
138
|
+
// Use ${KIPRJMOD} for project-relative paths
|
|
139
|
+
const relativePath = footprintLibPath.startsWith(projectDir)
|
|
140
|
+
? '${KIPRJMOD}' + footprintLibPath.slice(projectDir.length)
|
|
141
|
+
: footprintLibPath;
|
|
142
|
+
|
|
143
|
+
if (!existsSync(tablePath)) {
|
|
144
|
+
// Create new table
|
|
145
|
+
const content = generateFpLibTable(relativePath, libraryName, description);
|
|
146
|
+
await writeFile(tablePath, content, 'utf-8');
|
|
147
|
+
return { created: true, modified: false, path: tablePath };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Read existing table
|
|
151
|
+
const existingContent = await readFile(tablePath, 'utf-8');
|
|
152
|
+
|
|
153
|
+
if (libraryExistsInTable(existingContent, libraryName)) {
|
|
154
|
+
return { created: false, modified: false, path: tablePath };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add library to existing table
|
|
158
|
+
const updatedContent = addLibraryToTable(existingContent, libraryName, relativePath, 'fp', description);
|
|
159
|
+
await writeFile(tablePath, updatedContent, 'utf-8');
|
|
160
|
+
return { created: false, modified: true, path: tablePath };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get the symbol reference string for use in schematics
|
|
165
|
+
* @param symbolName - The symbol name within the library
|
|
166
|
+
* @param libraryName - Library name (default: LCSC)
|
|
167
|
+
* @returns Full reference like "LCSC:SymbolName"
|
|
168
|
+
*/
|
|
169
|
+
export function getSymbolReference(symbolName: string, libraryName: string = DEFAULT_LIBRARY_NAME): string {
|
|
170
|
+
return `${libraryName}:${symbolName}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get the footprint reference string for use in symbols/schematics
|
|
175
|
+
* @param footprintName - The footprint filename (without .kicad_mod)
|
|
176
|
+
* @param libraryName - Library name (default: LCSC)
|
|
177
|
+
* @returns Full reference like "LCSC:FootprintName"
|
|
178
|
+
*/
|
|
179
|
+
export function getFootprintReference(footprintName: string, libraryName: string = DEFAULT_LIBRARY_NAME): string {
|
|
180
|
+
return `${libraryName}:${footprintName}`;
|
|
181
|
+
}
|