Skip to content

Commit 7a96312

Browse files
committed
Feature Improvements
1. Sheet names exceeding 31 characters are now treated as warnings. 2. Originally defined sheet names are now added as columns to the table of contents.
1 parent 0b7d845 commit 7a96312

4 files changed

Lines changed: 40 additions & 41 deletions

File tree

src/excel-cli.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const messages = {
1313
sheetNameTooLong: 'Sheet name is too long (max 31 characters, current: {length} characters)',
1414
sheetNameInvalidChars: 'Contains invalid characters: {chars}',
1515
sheetNameWhitespace: 'Sheet name has leading or trailing whitespace.',
16+
sheetNameMayTruncate: 'Sheet name may be truncated in Excel.',
1617

1718
helpMessage: `
1819
SQL2Excel
@@ -124,6 +125,7 @@ const messages = {
124125
sheetNameTooLong: '시트명이 너무 깁니다 (최대 31자, 현재: {length}자)',
125126
sheetNameInvalidChars: '허용되지 않는 문자 포함: {chars}',
126127
sheetNameWhitespace: '시트명 앞뒤에 공백이 있습니다.',
128+
sheetNameMayTruncate: '엑셀에서 시트명이 잘릴 수 있습니다.',
127129

128130
helpMessage:
129131
`SQL2Excel
@@ -246,9 +248,7 @@ function validateSheetName(sheetName, skipLengthCheck = false) {
246248
return { valid: false, errors };
247249
}
248250

249-
if (!skipLengthCheck && sheetName.length > 31) {
250-
errors.push(msg.sheetNameTooLong.replace('{length}', sheetName.length));
251-
}
251+
// 31자 초과는 오류가 아니라 경고로 처리 (검증 실패에 포함하지 않음)
252252

253253
const foundInvalidChars = invalidChars.filter(char => sheetName.includes(char));
254254
if (foundInvalidChars.length > 0) {
@@ -516,11 +516,17 @@ async function validateQueryFile(options) {
516516

517517
if (sheetNameValidation.valid) {
518518
console.log(msg.sheetValidSuccess.replace('{index}', sheetIndex).replace('{name}', sheetName));
519+
if (!hasVariables && sheetName.length > 31) {
520+
console.warn(' - ' + msg.sheetNameTooLong.replace('{length}', sheetName.length) + ' ' + msg.sheetNameMayTruncate);
521+
}
519522
} else {
520523
console.error(msg.sheetValidFailed.replace('{index}', sheetIndex).replace('{name}', sheetName));
521524
sheetNameValidation.errors.forEach(error => {
522525
console.error(msg.sheetValidErrorItem.replace('{error}', error));
523526
});
527+
if (!hasVariables && sheetName.length > 31) {
528+
console.warn(' - ' + msg.sheetNameTooLong.replace('{length}', sheetName.length) + ' ' + msg.sheetNameMayTruncate);
529+
}
524530
console.error(msg.sheetValidAutoFix);
525531
hasValidationErrors = true;
526532
}
@@ -589,11 +595,17 @@ async function validateQueryFile(options) {
589595

590596
if (sheetNameValidation.valid) {
591597
console.log(msg.sheetValidSuccess.replace('{index}', sheetIndex).replace('{name}', sheetName));
598+
if (!hasVariables && sheetName.length > 31) {
599+
console.warn(' - ' + msg.sheetNameTooLong.replace('{length}', sheetName.length) + ' ' + msg.sheetNameMayTruncate);
600+
}
592601
} else {
593602
console.error(msg.sheetValidFailed.replace('{index}', sheetIndex).replace('{name}', sheetName));
594603
sheetNameValidation.errors.forEach(error => {
595604
console.error(msg.sheetValidErrorItem.replace('{error}', error));
596605
});
606+
if (!hasVariables && sheetName.length > 31) {
607+
console.warn(' - ' + msg.sheetNameTooLong.replace('{length}', sheetName.length) + ' ' + msg.sheetNameMayTruncate);
608+
}
597609
console.error(msg.sheetValidAutoFix);
598610
hasValidationErrors = true;
599611
}

src/excel-generator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class ExcelGenerator {
108108

109109
createdSheets.push({
110110
displayName: sheetDef.name,
111-
originalName: sheetDef.name,
111+
originalName: sheetDef.originalName || sheetDef.name,
112112
tabName: actualSheetName,
113113
recordCount: recordCount,
114114
aggregateColumn: sheetDef.aggregateColumn,

src/excel-style-helper.js

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -457,15 +457,11 @@ function populateTableOfContents(tocSheet, sheetNames) {
457457
// 기존 내용 모두 삭제
458458
tocSheet.spliceRows(1, tocSheet.rowCount);
459459

460-
// 헤더 추가 (쿼리문 컬럼 추가)
461-
tocSheet.addRow(['No', msg.sheetName, msg.records, 'Aggregate Info', 'Query', 'Note']);
460+
// 헤더 추가 (원본 시트명/쿼리문 컬럼 포함, Note 제거)
461+
tocSheet.addRow(['No', msg.sheetName, 'Original Name', msg.records, 'Aggregate Info', 'Query']);
462462

463463
// 시트 목록 추가
464464
sheetNames.forEach((obj, idx) => {
465-
// 시트명이 잘렸는지 확인
466-
const isTruncated = obj.originalName && obj.originalName !== obj.tabName;
467-
const noteText = isTruncated ? '(31자 초과로 잘림)' : '';
468-
469465
// 집계 정보 텍스트 생성 (템플릿 사용)
470466
let aggregateInfo = '';
471467

@@ -523,7 +519,14 @@ function populateTableOfContents(tocSheet, sheetNames) {
523519
}
524520
}
525521

526-
const row = tocSheet.addRow([idx + 1, obj.displayName, obj.recordCount || 0, aggregateInfo, queryText]);
522+
const row = tocSheet.addRow([
523+
idx + 1,
524+
obj.displayName,
525+
obj.originalName || '',
526+
obj.recordCount || 0,
527+
aggregateInfo,
528+
queryText
529+
]);
527530

528531
// 하이퍼링크 설정 - 실제 시트명(tabName) 사용
529532
const sheetNameForLink = obj.tabName.replace(/'/g, "''"); // 작은따옴표 이스케이프
@@ -559,8 +562,8 @@ function populateTableOfContents(tocSheet, sheetNames) {
559562
}
560563
}
561564

562-
// 데이터 건수 스타일링 및 하이퍼링크
563-
const recordCountCell = row.getCell(3);
565+
// 데이터 건수 스타일링 및 하이퍼링크 (이제 4번째 컬럼)
566+
const recordCountCell = row.getCell(4);
564567
const recordCountText = (obj.recordCount || 0).toString();
565568

566569
// 데이터 건수에도 하이퍼링크 적용
@@ -595,8 +598,8 @@ function populateTableOfContents(tocSheet, sheetNames) {
595598
recordCountCell.numFmt = '#,##0'; // 천 단위 구분자
596599
recordCountCell.alignment = { horizontal: 'right' };
597600

598-
// 집계 데이터 컬럼에 하이퍼링크 적용
599-
const aggregateCell = row.getCell(4);
601+
// 집계 데이터 컬럼에 하이퍼링크 적용 (이제 5번째 컬럼)
602+
const aggregateCell = row.getCell(5);
600603
if (aggregateInfo) {
601604
// 집계 정보에도 하이퍼링크 적용
602605
const aggregateFormula = `HYPERLINK("#'${sheetNameForLink}'!A1","${aggregateInfo.replace(/"/g, '""')}")`;
@@ -639,8 +642,8 @@ function populateTableOfContents(tocSheet, sheetNames) {
639642
aggregateCell.font = { color: { argb: '999999' } };
640643
}
641644

642-
// 쿼리문 컬럼 스타일링 (5번째 컬럼)
643-
const queryCell = row.getCell(5);
645+
// 쿼리문 컬럼 스타일링 (이제 6번째 컬럼)
646+
const queryCell = row.getCell(6);
644647
if (queryText) {
645648
queryCell.font = {
646649
size: 9,
@@ -655,35 +658,16 @@ function populateTableOfContents(tocSheet, sheetNames) {
655658
queryCell.value = '';
656659
queryCell.font = { color: { argb: '999999' } };
657660
}
658-
659-
// 비고 컬럼 스타일링 (6번째 컬럼)
660-
if (isTruncated) {
661-
row.getCell(6).font = {
662-
italic: true,
663-
color: { argb: 'D2691E' } // 주황색으로 경고 표시
664-
};
665-
666-
// 원본 시트명을 셀 주석으로 추가
667-
row.getCell(2).note = {
668-
texts: [
669-
{ text: msg.originalSheetName, font: { bold: true } },
670-
{ text: obj.originalName, font: { italic: true } },
671-
{ text: msg.actualTabName, font: { bold: true } },
672-
{ text: obj.tabName, font: { color: { argb: 'FF0000' } } },
673-
{ text: msg.sheetNameLimit, font: { size: 9, color: { argb: '666666' } } }
674-
]
675-
};
676-
}
677661
});
678662

679-
// 컬럼 설정 (쿼리문 컬럼 추가)
663+
// 컬럼 설정 (Original Name 추가, Note 제거)
680664
tocSheet.columns = [
681665
{ header: 'No', key: 'no', width: 6 },
682666
{ header: msg.sheetName, key: 'name', width: 25 },
667+
{ header: 'Original Name', key: 'original', width: 25 },
683668
{ header: msg.records, key: 'records', width: 12 },
684669
{ header: 'Aggregate Info', key: 'aggregate', width: 20 },
685-
{ header: 'Query', key: 'query', width: 40 },
686-
{ header: 'Note', key: 'note', width: 18 }
670+
{ header: 'Query', key: 'query', width: 40 }
687671
];
688672

689673
// 헤더 스타일

src/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,15 @@ async function main() {
268268

269269
let sql = variableProcessor.substituteVars(sheetDef.query, mergedVars, sheetDef.params || {});
270270
let sheetName = variableProcessor.substituteVars(sheetDef.name, mergedVars, sheetDef.params || {});
271+
const originalSheetNameCandidate = sheetName;
271272

272273
// 시트명 자동 수정 (변수 치환 후)
273274
const sheetNameValidation = queryParser.validateSheetName(sheetName, sheetIndex);
275+
274276
if (!sheetNameValidation.valid) {
275277
console.warn(`${msg.sheetNameAutoFix} #${sheetIndex + 1}):`);
276278
console.warn(`${msg.originalSheetName} "${sheetName}"`);
277-
279+
278280
// 허용되지 않는 문자 제거
279281
const invalidChars = ['\\', '/', '*', '?', '[', ']', ':'];
280282
invalidChars.forEach(char => {
@@ -346,7 +348,7 @@ async function main() {
346348

347349
createdSheetNames.push({
348350
displayName: sheetName,
349-
originalName: sheetName,
351+
originalName: originalSheetNameCandidate,
350352
tabName: sheetName,
351353
recordCount: recordCount,
352354
aggregateColumn: sheetDef.aggregateColumn,
@@ -358,6 +360,7 @@ async function main() {
358360
// 처리된 시트 정보 저장
359361
processedSheets.push({
360362
name: sheetName,
363+
originalName: originalSheetNameCandidate,
361364
data: result.recordset,
362365
style: sheetStyle,
363366
recordCount: recordCount,

0 commit comments

Comments
 (0)