Skip to content

Commit 7eca2d6

Browse files
authored
Merge pull request #19 from igapyon/tiga0421hbe
Node upstream との report / XLSX 出力 parity を強化する
2 parents 25fc3fe + 62306cf commit 7eca2d6

28 files changed

Lines changed: 743 additions & 110 deletions

TODO.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515

1616
### 直近の限定作業
1717

18+
- 再開ポイント:
19+
- ここまでで `dependency.xml` の report directory / report bundle ZIP / monthly SVG ZIP は Node upstream と byte-level parity 済み
20+
- ここまでで WBS XLSX / SVG / report ZIP 系の大きな簡略化は潰し、`mvn package` と opt-in Node parity が通っている
21+
- 次に再開するなら、report 系より `projectxlsx` の残差分を優先する
22+
- 具体的には `ProjectXlsxExport*` の editable cell styling、sheet theme、Project sheet の Settings section / merged range を upstream TypeScript と照合して実装へ反映する
23+
- その次の候補は `projectpatchjson` の warning / blocker details を upstream と構造比較すること
1824
- [x] 残作業を新規機能追加ではなく、既存範囲の確認 / docs 整理 / upstream 差分確認へ絞る
1925
- [x] `README.md`, `docs/remaining-migration-items.md`, `docs/upstream-class-mapping.md`, `docs/upstream-followup-log.md`, `docs/msprojectxml-import-design.md` の現在フェーズ表現を揃える
2026
- [ ] Agent Skills 側で `project_draft_view` 生成時に `planned_start` / `planned_finish` を入れる前提になっているか確認する
@@ -28,16 +34,19 @@
2834
- [x] `exportWorkbook(...)` が実 Excel OOXML zip ではなく独自 `mikuproject_xlsx_workbook_v1` 形式を返している問題を修正し、主要導線を OOXML ZIP へ切り替える
2935
- `exportWorkbookArchive(...)` / `importWorkbookArchive(...)` はあるが、report / CLI / Core API の主要導線から使われていない
3036
- [x] upstream の `dataValidations` と style descriptor の主要部を Java 側の OOXML build / parse に反映する
31-
- `formula`, `freezePane` はまだ Java 側 model / OOXML build / parse に十分反映されていない
37+
- [x] `formula`, `freezePane` Java 側 model / OOXML build / parse に反映する
3238
- 優先度 A: `projectxlsx`
33-
- `ProjectXlsxExport*` が upstream の列幅、merged range、data validation、editable cell styling、boolean option sheet、sheet theme を大きく省略している
39+
- [x] `ProjectXlsxExport*` の列幅、boolean data validation、boolean option sheet を upstream 寄りに補強する
40+
- `ProjectXlsxExport*` の editable cell styling、sheet theme、Project sheet の Settings section / merged range はまだ薄い
3441
- import 側も upstream の workbook validation / editable cell 前提とズレがないか、fixture round-trip だけでなく sheet 構造で確認する
3542
- 優先度 A: `wbsxlsx`
3643
- [x] WBS workbook の主要導線が独自 workbook bytes を出しており、`wbs.xlsx` というファイル名と実体が合っていない問題を修正する
3744
- [x] upstream 相当の row height、hidden columns、style、date band cell、progress band、summary / legend へ寄せる
38-
- `freeze` と byte-level の worksheet XML serialization / package XML までは未一致
45+
- [x] `dependency.xml` の Node parity で `wbs.xlsx` の OOXML ZIP / worksheet XML / styles XML / workbook XML が byte-level 一致するところまで寄せる
46+
- `freeze` / `formula` は Java 側 model / OOXML build / parse へ反映済み。WBS workbook 自体は現時点でこれらを使っていない
3947
- 優先度 B: `wbssvg`
40-
- 日付軸 / 月次カレンダー / style / bar 形状は修正を進めたが、label placement、viewport trim、dependency connector routing はまだ TypeScript 版より薄い
48+
- [x] `dependency.xml` の Node parity で `daily.svg` / `weekly.svg` / `monthly-calendar/*.svg` が byte-level 一致するところまで寄せる
49+
- 週次 SVG は viewport trim / label placement / dependency connector routing の主要部を Java 側へ反映済み
4150
- daily / weekly / monthly について、見た目の目視だけでなく class / shape / path 構造比較テストを追加する
4251
- 優先度 B: `projectpatchjson`
4352
- `first cut` 制限自体は upstream 由来だが、Java 側の warning 詳細や参照 blocker が薄くなりやすい
@@ -61,12 +70,16 @@
6170
- [x] `.xlsx` の主要導線を独自 `mikuproject_xlsx_workbook_v1` 出力から OOXML ZIP 出力へ切り替える
6271
- [x] `styles.xml` を固定 2 スタイルから upstream 相当の動的 style book 生成へ寄せる
6372
- [x] `wbs.xlsx` の worksheet model を upstream 相当の project info / task rows / legend / summary / progress band へ寄せる
64-
- [ ] `.xlsx` の worksheet XML / styles XML / workbook XML の byte-level parity を entry 単位で確認する
65-
- 現状: `dependency.xml` の opt-in Node parity は `wbs.md` まで一致し、`wbs.xlsx` で停止する
66-
- 残差分: worksheet XML の空白 / `xml:space` / empty cell serialization、package XML の entry 順と整形、timestamp 行、`formula` / `freezePane`
73+
- [x] `.xlsx` の worksheet XML / styles XML / workbook XML の byte-level parity を entry 単位で確認する
74+
- 現状: `dependency.xml` の opt-in Node parity は report directory 出力一式で一致する
75+
- [x] `dependency.xml` の opt-in Node parity で report bundle ZIP / monthly SVG ZIP の byte-level parity を確認する
76+
- `CoreApiReport` の bundle entry 順も upstream と同じ `wbs.xlsx`, `wbs.md`, `mermaid.mmd`, `daily.svg`, `weekly.svg`, `monthly-calendar/*.svg` に揃える
77+
- 残差分: Project XLSX 側の editable styling / sheet theme / Settings section などの workbook layout 差分は継続する
6778
- parity が難しい場合の例外は、差分理由を固定値、entry 順、JSON key order、XML serialization、数値丸め、locale / timezone、CLI diagnostics のどれかへ分類し、正規化比較へ逃げる箇所を TODO ではなく明示的な仕様として記録する
6879
- [ ] report 生成物全体の品質を再点検する
6980
- SVG の日付軸 / 月次カレンダーが簡略実装へ退化していたため、同じ report 系の XLSX / ZIP / directory export も信用しすぎない
81+
- [x] `dependency.xml` の report directory 出力で、SVG / XLSX / MD / MMD の Node 版 byte-level parity を確認する
82+
- [x] `dependency.xml` の report bundle ZIP / monthly SVG ZIP 出力で Node 版 byte-level parity を確認する
7083
- SVG は既存 TypeScript 版に寄せ、title 位置、style class、bar / milestone / phase の形状、dependency connector、weekly meta、monthly calendar の見た目を構造比較する
7184
- `export-report-dir`, `export-report-bundle`, `export-monthly-svg-zip`, `export-wbs-xlsx` を同一 fixture で出力し、entry 名、0 バイト有無、主要シート / 主要セル / SVG 構造を確認する
7285
- `wbs.xlsx` と standalone `export-wbs-xlsx` の内容が同等か確認する
@@ -77,11 +90,11 @@
7790
- [ ] XLSX / OOXML 系の簡略実装を upstream 相当へ戻す
7891
- [x] `CoreApiReport``wbs.xlsx` entry と `export-wbs-xlsx``XlsxWorkbookCodec.exportWorkbook(...)` の独自 `mikuproject_xlsx_workbook_v1` 形式を出しており、実 Excel workbook zip ではない問題を修正する
7992
- [x] `XlsxWorkbookCodec.exportWorkbookArchive(...)` / `importWorkbookArchive(...)` を report / CLI の主要導線から使う
80-
- `XlsxSheetLike` に upstream の `freezePane` がなく`XlsxCellLike` に upstream の `formula` がない
93+
- [x] `XlsxSheetLike` に upstream の `freezePane``XlsxCellLike` に upstream の `formula` を追加する
8194
- [x] `ExcelIoWorksheetBuild``dataValidations` を OOXML へ出していない問題を修正する
82-
- `ExcelIoWorksheetBuild``freezePane` を OOXML へ出していない
95+
- [x] `ExcelIoWorksheetBuild` / parse `freezePane` と formula cell を OOXML へ出し入れできるようにする
8396
- [x] `ExcelIoStylesBuild` が実際の `fillColor` / alignment / number format / wrap / border の組み合わせを十分に反映せず、ほぼ固定 style へ潰している問題を修正する
84-
- `ProjectXlsxExport*` が upstream の列幅、merged range、data validation、editable cell styling、boolean option sheet などを大きく省略している
97+
- `ProjectXlsxExport*` は列幅、boolean data validation、boolean option sheet を補強済み。editable cell styling、sheet theme、Project sheet の Settings section / merged range は継続
8598
- テストは byte size / decode だけでなく、zip entry、worksheet XML、styles XML、data validation、freeze pane、主要セル style を確認する
8699

87100
### 今週

docs/remaining-migration-items.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,11 @@ Java 版は、次の 3 系統で段階的に進める。
283283
3. report API を、upstream と対応づけて揃える
284284
- `WBS Markdown` / `SVG` / `WBS XLSX` / report bundle / report directory について、hierarchy / dependency fixture の主要出力比較を Java test で固定済み
285285
- option についても、`WBS Markdown` / `SVG` / `WBS XLSX` の direct API test と CLI test の両方で display / progress / holiday / label の反映を固定済み
286+
- `dependency.xml` の opt-in Node parity では report directory の `wbs.md` / `mermaid.mmd` / `daily.svg` / `weekly.svg` / `monthly-calendar/*.svg` / `wbs.xlsx` が byte-level 一致することを確認済み
287+
- 同じ opt-in Node parity で report bundle ZIP と monthly SVG ZIP も byte-level 一致することを確認済み
286288
- 現時点では report 周りの主要 regression は unit / core API / CLI の 3 層でまとまって通る状態にある
287289
- 直近のまとまった確認では `WbsMarkdownTest`, `WbsSvgTest`, `WbsXlsxTest`, `CoreApiPublicTest`, `MikuprojectCliTest` をまとめて通しており、report の主要導線はこの単位で回帰確認できる
288-
- 残る論点は、出力表現の細部差分をどこまで upstream へ寄せるか、各 report の option 契約と比較粒度をどこまで細かく保守するか、という精度面の詰めである
290+
- 残る論点は、各 report の option 契約と比較粒度をどこまで細かく保守するか、という精度面の詰めである
289291

290292
## 継承する仕様と保留する仕様
291293

docs/upstream-followup-log.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,13 @@ tests:
8282
CoreApiPublicTest.exposesWorkingReportApiSurface
8383
CoreApiPublicTest.exposesWorkingReportApiSurfaceForDependencyFixture
8484
CoreApiPublicTest.exposesWorkingReportApiSurfaceForHierarchyFixture
85+
MikuprojectNodeParityTest.comparesReportBundleZipBytesWithNodeUpstreamWhenEnabled
8586
MikuprojectCliTest.exportsReportBundleAndReportDirForFixturesThroughCli
8687
MikuprojectCliTest.appliesWbsOptionArgumentsToReportBundleAndWbsXlsx
8788
8889
diff summary:
8990
挙動差分:
90-
現時点で大きな差分は見当たらない。Java 側では report bundle の entry 構成、dependency / hierarchy fixture の主要内容、bundle 内 `wbs.xlsx` decode を確認済みである。
91+
現時点で大きな差分は見当たらない。Java 側では report bundle の entry 構成と entry 順、dependency / hierarchy fixture の主要内容、bundle 内 `wbs.xlsx` decode、`dependency.xml` の Node upstream bundle ZIP byte-level parity を確認済みである。
9192
命名差分:
9293
Java 側は `CoreApiReport` / `CoreApiReportAdapters` / `CoreApiReportPublic` に分割しているが、`report` 公開面との対応は追跡可能である。
9394
未移植差分:
@@ -98,6 +99,7 @@ diff summary:
9899
follow-up:
99100
- 実施した確認:
100101
`mvn test -Dtest=WbsMarkdownTest,WbsSvgTest,WbsXlsxTest,CoreApiPublicTest,MikuprojectCliTest`
102+
`MIKUPROJECT_RUN_NODE_PARITY=true mvn test -Dtest=MikuprojectNodeParityTest`
101103
- fixture:
102104
`vendor/mikuproject/testdata/dependency.xml`
103105
`vendor/mikuproject/testdata/hierarchy.xml`
@@ -438,23 +440,23 @@ tests:
438440
439441
diff summary:
440442
挙動差分:
441-
現時点で大きな差分は見当たらない。Java 側では workbook sheet 変換、editable field import、`ProjectModel` 構築、hierarchy fixture round-trip を確認済みである
443+
以前は列幅、boolean data validation、boolean option sheet、formula / freezePane 対応が薄かった。Java 側では `formula` / `freezePane` を workbook-like model と OOXML build / parse に追加し、Project XLSX export へ列幅、boolean data validation、boolean option sheet を反映した
442444
命名差分:
443445
Java 側は export / import に分割しているが、`ProjectXlsx` 公開面との対応は追跡可能である。
444446
未移植差分:
445-
現時点で顕在化している未移植差分は記録していない。今後 upstream で workbook sheet 構成や import 契約が変わった場合は再確認が必要である。
447+
editable cell styling、sheet theme、Project sheet の Settings section / merged range はまだ upstream より薄い。今後 upstream で workbook sheet 構成や import 契約が変わった場合は再確認が必要である。
446448
Java 側独自拡張:
447449
Core API wrapper や encode/decode 導線は upstream 本体の `project-xlsx.ts` ではなく、Java 側の wrapper 層として扱う。
448450
449451
follow-up:
450452
- 実施した確認:
451-
`mvn test -Dtest=ProjectXlsxTest,CoreApiWorkbookTest`
453+
`mvn test -Dtest=ProjectXlsxTest,ExcelIoTest,MikuprojectNodeParityTest`
452454
- fixture:
453455
`vendor/mikuproject/testdata/hierarchy.xml`
454456
- 次回の確認観点:
455-
upstream 側で workbook sheet 構成、editable field import、round-trip 契約が変わった場合は、unit と Core API wrapper の両方を見直す
457+
upstream 側で workbook sheet 構成、editable field styling、sheet theme、round-trip 契約が変わった場合は、unit と Core API wrapper の両方を見直す
456458
- `docs/remaining-migration-items.md` への反映:
457-
2026-04-20 時点では追加反映なし
459+
2026-04-21 時点で report directory byte-level parity と Project XLSX layout 補強の現在地を反映
458460
```
459461

460462
### 2026-04-20 `vendor/mikuproject/src/ts/core-api-workbook-xlsx.ts`

src/main/java/jp/igapyon/mikuproject/coreapi/CoreApiReport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ public List<ReportEntry> exportAllReportEntries(ProjectModel model, WbsMarkdownO
3838
public List<ReportEntry> exportAllReportEntries(ProjectModel model, WbsMarkdownOptions markdownOptions,
3939
WbsExportOptions xlsxOptions, NativeSvgOptions svgOptions) {
4040
List<ReportEntry> entries = new ArrayList<ReportEntry>();
41+
entries.add(new ReportEntry("wbs.xlsx", xlsxWorkbookCodec.exportWorkbook(wbsXlsx.exportWbsWorkbook(model, xlsxOptions))));
4142
entries.add(new ReportEntry("wbs.md", ExcelIoUtil.encodeUtf8(wbsMarkdown.exportWbsMarkdown(model, markdownOptions) + "\n")));
4243
entries.add(new ReportEntry("mermaid.mmd", ExcelIoUtil.encodeUtf8(msProjectXml.exportMermaidGantt(model) + "\n")));
43-
entries.add(new ReportEntry("wbs.xlsx", xlsxWorkbookCodec.exportWorkbook(wbsXlsx.exportWbsWorkbook(model, xlsxOptions))));
4444
entries.add(new ReportEntry("daily.svg", ExcelIoUtil.encodeUtf8(msProjectXml.exportNativeSvg(model, svgOptions) + "\n")));
4545
entries.add(new ReportEntry("weekly.svg", ExcelIoUtil.encodeUtf8(msProjectXml.exportWeeklyNativeSvg(model, svgOptions) + "\n")));
4646
MonthlyCalendarSvgArchive monthlyArchive = msProjectXml.exportMonthlyWbsCalendarSvgArchive(model, svgOptions);

src/main/java/jp/igapyon/mikuproject/excelio/ExcelIoNormalize.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import jp.igapyon.mikuproject.projectxlsx.XlsxCellLike;
1313
import jp.igapyon.mikuproject.projectxlsx.XlsxColumnLike;
1414
import jp.igapyon.mikuproject.projectxlsx.XlsxDataValidationLike;
15+
import jp.igapyon.mikuproject.projectxlsx.XlsxFreezePaneLike;
1516
import jp.igapyon.mikuproject.projectxlsx.XlsxRowLike;
1617
import jp.igapyon.mikuproject.projectxlsx.XlsxSheetLike;
1718
import jp.igapyon.mikuproject.projectxlsx.XlsxWorkbookLike;
@@ -117,6 +118,7 @@ private void normalizeSheet(XlsxSheetLike sheet) {
117118
normalizeDataValidation(dataValidation);
118119
}
119120
}
121+
normalizeFreezePane(sheet.freezePane);
120122
if (sheet.rows != null) {
121123
for (XlsxRowLike row : sheet.rows) {
122124
if (row == null) {
@@ -134,6 +136,23 @@ private void normalizeSheet(XlsxSheetLike sheet) {
134136
}
135137
}
136138

139+
private void normalizeFreezePane(XlsxFreezePaneLike freezePane) {
140+
if (freezePane == null) {
141+
return;
142+
}
143+
if (freezePane.rowSplit != null && freezePane.rowSplit.intValue() < 0) {
144+
throw new IllegalArgumentException("Freeze pane rowSplit must be zero or positive");
145+
}
146+
if (freezePane.colSplit != null && freezePane.colSplit.intValue() < 0) {
147+
throw new IllegalArgumentException("Freeze pane colSplit must be zero or positive");
148+
}
149+
if ((freezePane.rowSplit == null || freezePane.rowSplit.intValue() == 0)
150+
&& (freezePane.colSplit == null || freezePane.colSplit.intValue() == 0)) {
151+
freezePane.rowSplit = null;
152+
freezePane.colSplit = null;
153+
}
154+
}
155+
137156
private void normalizeDataValidation(XlsxDataValidationLike dataValidation) {
138157
if (dataValidation == null) {
139158
throw new IllegalArgumentException("Data validation must be defined");
@@ -173,6 +192,9 @@ private void normalizeCell(XlsxCellLike cell) {
173192
if (cell.value != null && !(cell.value instanceof String) && !(cell.value instanceof Number) && !(cell.value instanceof Boolean)) {
174193
throw new IllegalArgumentException("Cell value must be string, number, or boolean");
175194
}
195+
if (cell.formula != null && cell.formula.isEmpty()) {
196+
cell.formula = null;
197+
}
176198
if (cell.fontSize != null && cell.fontSize.intValue() <= 0) {
177199
throw new IllegalArgumentException("Cell fontSize must be a finite positive number");
178200
}

0 commit comments

Comments
 (0)