cornhsu-polymigrate 2.3.1 → 3.0.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/README.md CHANGED
@@ -3,27 +3,39 @@
3
3
  [![NuGet](https://img.shields.io/nuget/v/Cornhsu.PolyMigrate.svg?label=Cornhsu.PolyMigrate)](https://www.nuget.org/packages/Cornhsu.PolyMigrate)
4
4
  [![Downloads](https://img.shields.io/nuget/dt/Cornhsu.PolyMigrate.svg)](https://www.nuget.org/packages/Cornhsu.PolyMigrate)
5
5
  [![CI](https://github.com/HSU-YU-MING/cornhsu-polymigrate/actions/workflows/ci.yml/badge.svg)](https://github.com/HSU-YU-MING/cornhsu-polymigrate/actions/workflows/ci.yml)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/HSU-YU-MING/cornhsu-polymigrate/blob/main/LICENSE)
7
7
 
8
8
  > **The i18n-first static-site migrator** — the only site migrator that pairs your multilingual pages automatically.
9
9
 
10
- **[Project write-up](https://cornhsu.com/polymigrate) · [NuGet](https://www.nuget.org/packages/Cornhsu.PolyMigrate) · [繁體中文說明](README.zh-Hant.md) · MIT**
10
+ **[Project write-up](https://cornhsu.com/polymigrate) · [NuGet](https://www.nuget.org/packages/Cornhsu.PolyMigrate) · [繁體中文說明](https://github.com/HSU-YU-MING/cornhsu-polymigrate/blob/main/README.zh-Hant.md) · MIT**
11
11
 
12
12
  PolyMigrate turns legacy dynamic sites (old PHP sites and the like) into clean, static-site-ready
13
13
  Markdown — and it treats multilingual content as a first-class concern, not an afterthought.
14
14
  Config-driven, fully offline-rerunnable, built on .NET.
15
15
 
16
- **Status: 2.3.** The extraction pipeline, pairing, verification, thumbnails and orphan-page
16
+ **Status: 3.0.** The extraction pipeline, pairing, verification, thumbnails and orphan-page
17
17
  recovery are complete and validated against a real full-site migration (see below). The **CLI
18
- surface and the Phase output contracts remain stable** (unchanged since 1.0): new features bump
19
- the minor version, fixes bump the patch. 2.0 was an engineering release — it narrowed the
18
+ surface, exit codes, Phase output contracts and YAML config are frozen within a major version**:
19
+ new features bump the minor version, fixes bump the patch, and anything that changes one of those
20
+ four surfaces waits for a major. **3.0 is such a release** — it changes exit codes in a few cases
21
+ so they follow the 0 / 1 / 2 / 130 contract (an orphan probe that got no usable answer, a failed
22
+ npm launcher and an interrupted command no longer look like success), gives each section its own
23
+ orphan slug list (`orphan_slugs.<section>.txt`), keeps animated GIF/WebP thumbnails animated, and
24
+ adds probe/fetch entry points and cancellable overloads to the library. Config fields, CSV columns
25
+ and frontmatter fields are unchanged, but a config with an invalid value (a CSS selector, a regex, a
26
+ path that isn't a single segment, a malformed URL or language tag, a key left without a value…) is now
27
+ rejected when it is loaded, by every command, instead of when some page or command happens to use it, and a probe in
28
+ which only some candidates got no answer exits 1 (the list is written, but may be incomplete).
29
+ **Read the 3.0 upgrade checklist in the CHANGELOG before upgrading**.
30
+ Earlier history: 2.0 was an engineering release — it narrowed the
20
31
  `Cornhsu.PolyMigrate.Core` public .NET API to its intended entry points and removed unused
21
- config fields; 2.1 fixed up the release pipeline (arm64 npm packages, symbol packages).
32
+ config fields; 2.1 fixed up the release pipeline (arm64 npm packages, symbol packages) — though
33
+ the npm launcher could only start the Windows/Linux arm64 builds from 2.3.1 on.
22
34
  2.2 closed a blind spot in `verify` — pages nothing links to were reported as fine, because a
23
35
  link-following audit never reaches them — and added `slugs` for keeping up with a source site
24
36
  that is still being updated. **Upgrading to 2.2 can turn a passing `verify` into exit 1**.
25
37
  2.3 reads the source site's own `hreflang` declarations (see below) and reports them in a new
26
- `hreflang_map.csv`. See the [CHANGELOG](CHANGELOG.md) for the migration notes.
38
+ `hreflang_map.csv`. See the [CHANGELOG](https://github.com/HSU-YU-MING/cornhsu-polymigrate/blob/main/CHANGELOG.md) for the migration notes.
27
39
 
28
40
  ## Why
29
41
 
@@ -62,6 +74,8 @@ A declaration is only used for pairing if it:
62
74
  - resolves to a page actually in your mirror — **same host included**, since plenty of sites put
63
75
  the English version on `en.example.org`, where a path-only comparison would make a page its
64
76
  own translation,
77
+ - has a content page at both ends — neither end is a site-level page (a language picker and the
78
+ like, which isn't anyone's translation),
65
79
  - and **isn't one of many same-language pages claiming the same target**. This is the classic
66
80
  failure: one `<link>` pasted into the site-wide template, so every Chinese page declares the
67
81
  English homepage as its English version. One page cannot be the translation of three others,
@@ -74,7 +88,7 @@ Everything declared — including the rejected ones — is written to `hreflang_
74
88
  Pairs are still only ever *suggested*: `hreflang` raises the evidence quality, it does not
75
89
  switch the tool into merging pages on its own. That is a deliberate call, not an unfinished
76
90
  one — the measurement behind it, and what would have to change to revisit it, are recorded in
77
- [docs/hreflang_量測與決策.md](docs/hreflang_量測與決策.md).
91
+ [docs/hreflang_量測與決策.md](https://github.com/HSU-YU-MING/cornhsu-polymigrate/blob/main/docs/hreflang_%E9%87%8F%E6%B8%AC%E8%88%87%E6%B1%BA%E7%AD%96.md).
78
92
 
79
93
  ## Battle-tested defaults
80
94
 
@@ -123,7 +137,7 @@ Two channels — pick whichever runtime you already have. **Same tool, same beha
123
137
 
124
138
  ```
125
139
  npx cornhsu-polymigrate extract site.yaml # Node — no .NET install needed
126
- dotnet tool install -g Cornhsu.PolyMigrate # .NET
140
+ dotnet tool install -g Cornhsu.PolyMigrate # .NET 10
127
141
  ```
128
142
 
129
143
  > The npm build ships a self-contained native binary and only downloads the one
@@ -134,13 +148,24 @@ dotnet tool install -g Cornhsu.PolyMigrate # .NET
134
148
  ```
135
149
  dotnet tool install -g Cornhsu.PolyMigrate
136
150
  polymigrate extract site.yaml # mirror HTML -> frontmatter Markdown + inventories
137
- polymigrate verify out/ # link/media/frontmatter/unlinked-page audit, CI exit codes
151
+ polymigrate verify . # link/media/frontmatter/unlinked-page audit, CI exit codes
138
152
  polymigrate thumbs site.yaml # EXIF-corrected, width-capped thumbnails
139
- polymigrate probe-orphans site.yaml --section news --years 2021-2023
140
- polymigrate fetch-orphans site.yaml --section news
153
+ polymigrate probe-orphans site.yaml --section news --years 2021-2023 # -> orphan_slugs.news.txt
154
+ polymigrate fetch-orphans site.yaml --section news # reads orphan_slugs.news.txt
141
155
  polymigrate slugs . --section news # slugs already mirrored under ./raw (pipeable)
142
156
  ```
143
157
 
158
+ > **Animated GIF/WebP thumbnails keep every frame — within limits.** Scaling an animation frame by
159
+ > frame costs memory and time in proportion to frames × width × height (measured: a 400-frame
160
+ > 1300×900 GIF took 39 s and 3.2 GB). So an animation over **50 megapixels** in total, or one whose
161
+ > animated thumbnail would be larger than the original, gets a first-frame thumbnail instead — the
162
+ > same result as before 3.0 (an animation that needs no scaling is copied as-is instead). At the limit it takes about 6 s and a 250–400 MB memory peak, depending on the content
163
+ > (two 100-frame 1000×500 GIFs measured 250 MB and 384 MB). The `thumbs` summary lists
164
+ > which files got a first frame and why; neither case is a failure or changes the exit code.
165
+ > Still images get a limit too: one over **179 megapixels** (Pillow's decompression-bomb threshold —
166
+ > a 270 KB GIF can declare 20000×20000, and decoding it took 14.8 s and 1.6 GB) is not decoded at all and
167
+ > counts as a failed thumbnail (exit 1). 100-megapixel camera originals are well within it.
168
+
144
169
  > **`raw/` is the one thing you cannot regenerate.** Re-crawling a site takes hours at a
145
170
  > polite request rate, and the source site may be switched off the moment you finish
146
171
  > migrating — at which point your mirror is the only copy left. No PolyMigrate command
@@ -148,10 +173,10 @@ polymigrate slugs . --section news # slugs already mirrored under ./raw
148
173
  > off, and back them up if the source is going away.
149
174
 
150
175
  > No .NET? Every command above also runs with **zero install** via npm — just prefix it:
151
- > `npx cornhsu-polymigrate extract site.yaml`, `npx cornhsu-polymigrate verify out/`, and so on.
176
+ > `npx cornhsu-polymigrate extract site.yaml`, `npx cornhsu-polymigrate verify .`, and so on.
152
177
 
153
178
  One YAML config per site describes everything site-specific — see
154
- [examples/ibps-austin.yaml](examples/ibps-austin.yaml) for a fully-annotated real example.
179
+ [examples/ibps-austin.yaml](https://github.com/HSU-YU-MING/cornhsu-polymigrate/blob/main/examples/ibps-austin.yaml) for a fully-annotated real example.
155
180
 
156
181
  ```yaml
157
182
  config_version: 1
@@ -185,8 +210,16 @@ if (report.HasErrors) { /* unsafe paths were skipped; see path_issues.csv */ }
185
210
 
186
211
  var verify = PolyMigrator.Verify("out/"); // no config needed; reads Phase 2 output only
187
212
  Console.WriteLine($"{verify.Errors} errors, {verify.Warnings} warnings");
213
+
214
+ // Orphan pages (probe-orphans / fetch-orphans). Nothing is written until you decide to.
215
+ var probe = await migrator.ProbeOrphansAsync("out/", "news", 2021, 2023);
216
+ if (probe.NoUsableAnswers) { /* the site never answered: an empty list means nothing */ }
217
+ else { await migrator.FetchOrphansAsync("out/", "news", probe.Found); }
188
218
  ```
189
219
 
220
+ Extract, Verify and GenerateThumbnails also have overloads that take a `CancellationToken`
221
+ (the probe/fetch methods take one as their last parameter).
222
+
190
223
  ## Layout
191
224
 
192
225
  | Path | Contents |
@@ -214,9 +247,9 @@ signals in priority order, why "just read `hreflang`" often misses on real legac
214
247
  (measured on one: 528 pages, **0** declarations, sitemap 404), the five gates a declaration has to
215
248
  pass before it is usable at all, and why URL string similarity is the wrong tool for this job.
216
249
 
217
- License: [MIT](LICENSE). All dependencies are MIT/BSD/Apache-2.0
250
+ License: [MIT](https://github.com/HSU-YU-MING/cornhsu-polymigrate/blob/main/LICENSE). All dependencies are MIT/BSD/Apache-2.0
218
251
  (imaging via **Magick.NET**; ImageSharp was dropped when its 4.x line began requiring a
219
- license key at build time). See [THIRD-PARTY-NOTICES](THIRD-PARTY-NOTICES.md).
252
+ license key at build time). See [THIRD-PARTY-NOTICES](https://github.com/HSU-YU-MING/cornhsu-polymigrate/blob/main/THIRD-PARTY-NOTICES.md).
220
253
 
221
254
  PolyMigrate fetches pages from the site you point it at, and can be configured to send
222
255
  cookies past a bot challenge. **It assumes the site is yours, or that you are migrating
@@ -1,49 +1,307 @@
1
1
  # Third-party notices
2
2
 
3
3
  PolyMigrate itself is licensed under the [MIT License](LICENSE). It builds on the
4
- following third-party packages. All of their licences are compatible with MIT, and
5
- none of them are copyleft.
4
+ third-party components below. All of their licences are compatible with MIT, and none
5
+ of them are copyleft. The full licence texts are at the end of this file.
6
6
 
7
- | Package | Licence | Project |
7
+ ## What each channel ships
8
+
9
+ Whether a channel redistributes a component — and so has to carry its notices — depends
10
+ on how it is packaged:
11
+
12
+ | Channel | What is inside | Notices shipped with it |
8
13
  |---|---|---|
9
- | AngleSharp | MIT | https://anglesharp.github.io/ |
10
- | ReverseMarkdown | MIT | https://github.com/mysticmind/reversemarkdown-net |
11
- | YamlDotNet | MIT | https://github.com/aaubry/YamlDotNet |
12
- | Magick.NET (`Magick.NET-Q8-AnyCPU`, `Magick.NET.Core`) | Apache-2.0 | https://github.com/dlemstra/Magick.NET |
13
-
14
- ## Why this file exists
15
-
16
- For the NuGet packages (`Cornhsu.PolyMigrate`, `Cornhsu.PolyMigrate.Core`) the
17
- dependencies are resolved by NuGet and each package carries its own licence — nothing
18
- further is required from us.
19
-
20
- **The npm channel is different.** `cornhsu-polymigrate` ships a self-contained native
21
- binary that embeds `Magick.Native-Q8-*` directly, which makes it a redistribution of
22
- Magick.NET and of ImageMagick. Magick.NET is Apache-2.0 and its package includes a
23
- `Notice.txt`, so section 4(d) of the Apache License applies: that notice must travel
24
- with the redistribution.
25
-
26
- ## Magick.NET / ImageMagick
27
-
28
- > Copyright Dirk Lemstra, https://github.com/dlemstra/Magick.NET
29
- > Licensed under the Apache License, Version 2.0.
30
- >
31
- > Bundles ImageMagick, used under the ImageMagick License —
32
- > https://imagemagick.org/license/
33
-
34
- The full, authoritative notice ships inside the Magick.NET package as `Notice.txt`
35
- (and covers the exact ImageMagick version bundled by that release). Reproduce that
36
- file, not this summary, when redistributing.
37
-
38
- **This is automated, not a checklist item.** `npm/prepare.mjs` resolves the actual
39
- Magick.NET version from `project.assets.json`, reads `Notice.txt` from the NuGet cache,
40
- and writes it into every platform package as `NOTICE.txt` — **aborting the build if it
41
- cannot find it**. `release.yml` then verifies the built packages really carry it. A
42
- licence obligation that depends on someone remembering a checklist is one that will
14
+ | **`Cornhsu.PolyMigrate`** — .NET tool on NuGet | `PackAsTool` puts the whole publish output into the package (`tools/net10.0/any/`): every dependency DLL **and** Magick.NET's native libraries for all platforms (`runtimes/*/native/Magick.Native-Q8-*`, which contain ImageMagick). The tool package itself is a redistribution of all of them. | `NOTICE.txt` (Magick.NET's `Notice.txt`, verbatim) and this file, at the package root |
15
+ | **`Cornhsu.PolyMigrate.Core`** — library on NuGet | `PolyMigrate.Core.dll` only. Its dependencies are declared, not bundled: NuGet resolves them on the consumer's side, and each package carries its own licence. | Nothing further is required |
16
+ | **npm** — `cornhsu-polymigrate` and `@cornhsu/polymigrate-<os>-<cpu>` | Each platform package is a self-contained executable: every dependency DLL, the `Magick.Native-Q8-*` for that platform, **and the .NET runtime**. The main package holds only the launcher script. | Platform packages: `NOTICE.txt`, this file and `LICENSE`. Main package: this file and `LICENSE` |
17
+
18
+ ## Components
19
+
20
+ | Component | Licence | Copyright | Project | Shipped in |
21
+ |---|---|---|---|---|
22
+ | AngleSharp | MIT | Copyright (c) 2013 - 2026 AngleSharp | https://anglesharp.github.io/ | tool, npm |
23
+ | ReverseMarkdown | MIT | Copyright (c) 2015 Babu Annamalai | https://github.com/mysticmind/reversemarkdown-net | tool, npm |
24
+ | YamlDotNet | MIT | Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors | https://github.com/aaubry/YamlDotNet | tool, npm |
25
+ | Magick.NET (`Magick.NET-Q8-AnyCPU`, `Magick.NET.Core` and the native `Magick.Native-Q8-*`) | Apache-2.0 | Copyright 2013-2026 Dirk Lemstra | https://github.com/dlemstra/Magick.NET | tool, npm |
26
+ | ImageMagick and the libraries built into `Magick.Native-Q8-*` | ImageMagick License; each bundled library under its own licence | ImageMagick Studio LLC and the respective authors — see `NOTICE.txt` | https://imagemagick.org/ | tool, npm |
27
+ | .NET runtime | MIT | Copyright (c) .NET Foundation and Contributors | https://github.com/dotnet/runtime | npm only |
28
+
29
+ The copyright lines are taken from each project's own licence file (Magick.NET: its package
30
+ metadata) at the versions this release resolves: AngleSharp 1.7.2, ReverseMarkdown 6.2.1,
31
+ YamlDotNet 18.1.0, Magick.NET 14.16.0. The exact versions inside a given package are listed
32
+ in its `polymigrate.deps.json`.
33
+
34
+ The .NET runtime contains third-party code of its own; its notices are at
35
+ https://github.com/dotnet/runtime/blob/main/THIRD-PARTY-NOTICES.TXT.
36
+
37
+ ## Magick.NET and ImageMagick: `NOTICE.txt`
38
+
39
+ Magick.NET is Apache-2.0 and its package includes a `Notice.txt`, so section 4(d) of the
40
+ Apache License applies: whoever redistributes it must pass that notice on. The file also
41
+ reproduces the ImageMagick License and the licence of every library built into the native
42
+ binaries, for the exact ImageMagick version of that Magick.NET release — which is why the
43
+ packages ship the file itself, not a summary of it.
44
+
45
+ **This is automated, not a checklist item.** Both channels that redistribute Magick.NET
46
+ resolve the Magick.NET version that was actually restored, read `Notice.txt` from the NuGet
47
+ cache, and ship it as `NOTICE.txt` — **and fail if they cannot find it**:
48
+
49
+ - **tool package:** the `_PackMagickNotice` target in `src/PolyMigrate.Cli/PolyMigrate.Cli.csproj`
50
+ fails `dotnet pack`;
51
+ - **npm:** `npm/prepare.mjs` aborts, and `release.yml` then checks that every built package
52
+ really carries `NOTICE.txt` and this file.
53
+
54
+ A licence obligation that depends on someone remembering a checklist is one that will
43
55
  eventually be missed.
44
56
 
45
57
  ## Keeping this current
46
58
 
47
- This file lists licences as of the versions pinned in `src/*/*.csproj`. **When a
48
- dependency is added, removed, or bumped to a version with a different licence, update
49
- this file in the same commit** — Dependabot bumps will not do it for you.
59
+ This file describes what the CLI's build output actually contains
60
+ (`src/PolyMigrate.Cli/bin/Release/net10.0`, listed in `polymigrate.deps.json`), not only what the
61
+ `.csproj` files reference: a build-time package can still leak a runtime DLL into the output.
62
+ **When a dependency is added, removed, or bumped, check the output and update this file in the
63
+ same commit** — Dependabot bumps will not do it for you.
64
+
65
+ ## Licence texts
66
+
67
+ ### MIT License
68
+
69
+ Applies to AngleSharp, ReverseMarkdown, YamlDotNet and the .NET runtime.
70
+
71
+ ```text
72
+ MIT License
73
+
74
+ Copyright (c) 2013 - 2026 AngleSharp
75
+ Copyright (c) 2015 Babu Annamalai
76
+ Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors
77
+ Copyright (c) .NET Foundation and Contributors
78
+
79
+ Permission is hereby granted, free of charge, to any person obtaining a copy
80
+ of this software and associated documentation files (the "Software"), to deal
81
+ in the Software without restriction, including without limitation the rights
82
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
83
+ copies of the Software, and to permit persons to whom the Software is
84
+ furnished to do so, subject to the following conditions:
85
+
86
+ The above copyright notice and this permission notice shall be included in all
87
+ copies or substantial portions of the Software.
88
+
89
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
90
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
91
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
92
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
93
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
94
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
95
+ SOFTWARE.
96
+ ```
97
+
98
+ ### Apache License 2.0
99
+
100
+ Applies to Magick.NET (`Magick.NET-Q8-AnyCPU`, `Magick.NET.Core` and the native
101
+ `Magick.Native-Q8-*`), Copyright 2013-2026 Dirk Lemstra. ImageMagick and the libraries built
102
+ into the native binaries are under their own licences, reproduced in full in `NOTICE.txt`.
103
+
104
+ ```text
105
+
106
+ Apache License
107
+ Version 2.0, January 2004
108
+ http://www.apache.org/licenses/
109
+
110
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
111
+
112
+ 1. Definitions.
113
+
114
+ "License" shall mean the terms and conditions for use, reproduction,
115
+ and distribution as defined by Sections 1 through 9 of this document.
116
+
117
+ "Licensor" shall mean the copyright owner or entity authorized by
118
+ the copyright owner that is granting the License.
119
+
120
+ "Legal Entity" shall mean the union of the acting entity and all
121
+ other entities that control, are controlled by, or are under common
122
+ control with that entity. For the purposes of this definition,
123
+ "control" means (i) the power, direct or indirect, to cause the
124
+ direction or management of such entity, whether by contract or
125
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
126
+ outstanding shares, or (iii) beneficial ownership of such entity.
127
+
128
+ "You" (or "Your") shall mean an individual or Legal Entity
129
+ exercising permissions granted by this License.
130
+
131
+ "Source" form shall mean the preferred form for making modifications,
132
+ including but not limited to software source code, documentation
133
+ source, and configuration files.
134
+
135
+ "Object" form shall mean any form resulting from mechanical
136
+ transformation or translation of a Source form, including but
137
+ not limited to compiled object code, generated documentation,
138
+ and conversions to other media types.
139
+
140
+ "Work" shall mean the work of authorship, whether in Source or
141
+ Object form, made available under the License, as indicated by a
142
+ copyright notice that is included in or attached to the work
143
+ (an example is provided in the Appendix below).
144
+
145
+ "Derivative Works" shall mean any work, whether in Source or Object
146
+ form, that is based on (or derived from) the Work and for which the
147
+ editorial revisions, annotations, elaborations, or other modifications
148
+ represent, as a whole, an original work of authorship. For the purposes
149
+ of this License, Derivative Works shall not include works that remain
150
+ separable from, or merely link (or bind by name) to the interfaces of,
151
+ the Work and Derivative Works thereof.
152
+
153
+ "Contribution" shall mean any work of authorship, including
154
+ the original version of the Work and any modifications or additions
155
+ to that Work or Derivative Works thereof, that is intentionally
156
+ submitted to Licensor for inclusion in the Work by the copyright owner
157
+ or by an individual or Legal Entity authorized to submit on behalf of
158
+ the copyright owner. For the purposes of this definition, "submitted"
159
+ means any form of electronic, verbal, or written communication sent
160
+ to the Licensor or its representatives, including but not limited to
161
+ communication on electronic mailing lists, source code control systems,
162
+ and issue tracking systems that are managed by, or on behalf of, the
163
+ Licensor for the purpose of discussing and improving the Work, but
164
+ excluding communication that is conspicuously marked or otherwise
165
+ designated in writing by the copyright owner as "Not a Contribution."
166
+
167
+ "Contributor" shall mean Licensor and any individual or Legal Entity
168
+ on behalf of whom a Contribution has been received by Licensor and
169
+ subsequently incorporated within the Work.
170
+
171
+ 2. Grant of Copyright License. Subject to the terms and conditions of
172
+ this License, each Contributor hereby grants to You a perpetual,
173
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
174
+ copyright license to reproduce, prepare Derivative Works of,
175
+ publicly display, publicly perform, sublicense, and distribute the
176
+ Work and such Derivative Works in Source or Object form.
177
+
178
+ 3. Grant of Patent License. Subject to the terms and conditions of
179
+ this License, each Contributor hereby grants to You a perpetual,
180
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
181
+ (except as stated in this section) patent license to make, have made,
182
+ use, offer to sell, sell, import, and otherwise transfer the Work,
183
+ where such license applies only to those patent claims licensable
184
+ by such Contributor that are necessarily infringed by their
185
+ Contribution(s) alone or by combination of their Contribution(s)
186
+ with the Work to which such Contribution(s) was submitted. If You
187
+ institute patent litigation against any entity (including a
188
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
189
+ or a Contribution incorporated within the Work constitutes direct
190
+ or contributory patent infringement, then any patent licenses
191
+ granted to You under this License for that Work shall terminate
192
+ as of the date such litigation is filed.
193
+
194
+ 4. Redistribution. You may reproduce and distribute copies of the
195
+ Work or Derivative Works thereof in any medium, with or without
196
+ modifications, and in Source or Object form, provided that You
197
+ meet the following conditions:
198
+
199
+ (a) You must give any other recipients of the Work or
200
+ Derivative Works a copy of this License; and
201
+
202
+ (b) You must cause any modified files to carry prominent notices
203
+ stating that You changed the files; and
204
+
205
+ (c) You must retain, in the Source form of any Derivative Works
206
+ that You distribute, all copyright, patent, trademark, and
207
+ attribution notices from the Source form of the Work,
208
+ excluding those notices that do not pertain to any part of
209
+ the Derivative Works; and
210
+
211
+ (d) If the Work includes a "NOTICE" text file as part of its
212
+ distribution, then any Derivative Works that You distribute must
213
+ include a readable copy of the attribution notices contained
214
+ within such NOTICE file, excluding those notices that do not
215
+ pertain to any part of the Derivative Works, in at least one
216
+ of the following places: within a NOTICE text file distributed
217
+ as part of the Derivative Works; within the Source form or
218
+ documentation, if provided along with the Derivative Works; or,
219
+ within a display generated by the Derivative Works, if and
220
+ wherever such third-party notices normally appear. The contents
221
+ of the NOTICE file are for informational purposes only and
222
+ do not modify the License. You may add Your own attribution
223
+ notices within Derivative Works that You distribute, alongside
224
+ or as an addendum to the NOTICE text from the Work, provided
225
+ that such additional attribution notices cannot be construed
226
+ as modifying the License.
227
+
228
+ You may add Your own copyright statement to Your modifications and
229
+ may provide additional or different license terms and conditions
230
+ for use, reproduction, or distribution of Your modifications, or
231
+ for any such Derivative Works as a whole, provided Your use,
232
+ reproduction, and distribution of the Work otherwise complies with
233
+ the conditions stated in this License.
234
+
235
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
236
+ any Contribution intentionally submitted for inclusion in the Work
237
+ by You to the Licensor shall be under the terms and conditions of
238
+ this License, without any additional terms or conditions.
239
+ Notwithstanding the above, nothing herein shall supersede or modify
240
+ the terms of any separate license agreement you may have executed
241
+ with Licensor regarding such Contributions.
242
+
243
+ 6. Trademarks. This License does not grant permission to use the trade
244
+ names, trademarks, service marks, or product names of the Licensor,
245
+ except as required for reasonable and customary use in describing the
246
+ origin of the Work and reproducing the content of the NOTICE file.
247
+
248
+ 7. Disclaimer of Warranty. Unless required by applicable law or
249
+ agreed to in writing, Licensor provides the Work (and each
250
+ Contributor provides its Contributions) on an "AS IS" BASIS,
251
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
252
+ implied, including, without limitation, any warranties or conditions
253
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
254
+ PARTICULAR PURPOSE. You are solely responsible for determining the
255
+ appropriateness of using or redistributing the Work and assume any
256
+ risks associated with Your exercise of permissions under this License.
257
+
258
+ 8. Limitation of Liability. In no event and under no legal theory,
259
+ whether in tort (including negligence), contract, or otherwise,
260
+ unless required by applicable law (such as deliberate and grossly
261
+ negligent acts) or agreed to in writing, shall any Contributor be
262
+ liable to You for damages, including any direct, indirect, special,
263
+ incidental, or consequential damages of any character arising as a
264
+ result of this License or out of the use or inability to use the
265
+ Work (including but not limited to damages for loss of goodwill,
266
+ work stoppage, computer failure or malfunction, or any and all
267
+ other commercial damages or losses), even if such Contributor
268
+ has been advised of the possibility of such damages.
269
+
270
+ 9. Accepting Warranty or Additional Liability. While redistributing
271
+ the Work or Derivative Works thereof, You may choose to offer,
272
+ and charge a fee for, acceptance of support, warranty, indemnity,
273
+ or other liability obligations and/or rights consistent with this
274
+ License. However, in accepting such obligations, You may act only
275
+ on Your own behalf and on Your sole responsibility, not on behalf
276
+ of any other Contributor, and only if You agree to indemnify,
277
+ defend, and hold each Contributor harmless for any liability
278
+ incurred by, or claims asserted against, such Contributor by reason
279
+ of your accepting any such warranty or additional liability.
280
+
281
+ END OF TERMS AND CONDITIONS
282
+
283
+ APPENDIX: How to apply the Apache License to your work.
284
+
285
+ To apply the Apache License to your work, attach the following
286
+ boilerplate notice, with the fields enclosed by brackets "[]"
287
+ replaced with your own identifying information. (Don't include
288
+ the brackets!) The text should be enclosed in the appropriate
289
+ comment syntax for the file format. We also recommend that a
290
+ file or class name and description of purpose be included on the
291
+ same "printed page" as the copyright notice for easier
292
+ identification within third-party archives.
293
+
294
+ Copyright [yyyy] [name of copyright owner]
295
+
296
+ Licensed under the Apache License, Version 2.0 (the "License");
297
+ you may not use this file except in compliance with the License.
298
+ You may obtain a copy of the License at
299
+
300
+ http://www.apache.org/licenses/LICENSE-2.0
301
+
302
+ Unless required by applicable law or agreed to in writing, software
303
+ distributed under the License is distributed on an "AS IS" BASIS,
304
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
305
+ See the License for the specific language governing permissions and
306
+ limitations under the License.
307
+ ```
@@ -34,13 +34,27 @@ const TARGETS = {
34
34
  const key = `${process.platform} ${process.arch}`;
35
35
  const target = TARGETS[key];
36
36
 
37
- if (!target) {
37
+ // 啟動腳本自己失敗 = PolyMigrate 根本沒有執行 = error,所以是 2(3.0 起)。
38
+ // 2.3.1 以前是 1,和 CLI 契約的「1 = 只有 warning」撞號:依「0/1 放行」的 CI 會把「工具沒跑」當成通過
39
+ // (2.1.0~2.3.0 的 arm64 事故走的就是這條路)。訊息用英文(與 CLI 其他輸出一致),明講工具沒有執行。
40
+ const LAUNCHER_FAILURE_EXIT = 2;
41
+
42
+ function fail(...lines) {
38
43
  console.error(
39
- `Cornhsu.PolyMigrate 沒有 ${key} 的預先建置版本。\n` +
40
- `目前支援:${Object.keys(TARGETS).join("、")}\n` +
41
- `其他平台請改用 .NET 版本:dotnet tool install -g Cornhsu.PolyMigrate`
44
+ [
45
+ `error: ${lines[0]} PolyMigrate did not run.`,
46
+ ...lines.slice(1),
47
+ ].join("\n")
48
+ );
49
+ process.exit(LAUNCHER_FAILURE_EXIT);
50
+ }
51
+
52
+ if (!target) {
53
+ fail(
54
+ `no prebuilt binary for ${key}.`,
55
+ `Supported platforms: ${Object.keys(TARGETS).join(", ")}.`,
56
+ "On other platforms, use the .NET tool instead: dotnet tool install -g Cornhsu.PolyMigrate"
42
57
  );
43
- process.exit(1);
44
58
  }
45
59
 
46
60
  let binary;
@@ -50,19 +64,22 @@ try {
50
64
  const manifest = require.resolve(`${target.pkg}/package.json`);
51
65
  binary = path.join(path.dirname(manifest), "bin", target.bin);
52
66
  } catch {
53
- console.error(
54
- `找不到平台套件 ${target.pkg}。\n` +
55
- `可能是安裝時加了 --no-optional,或安裝過程中斷。\n` +
56
- `請重新安裝:npm install cornhsu-polymigrate`
67
+ fail(
68
+ `the platform package ${target.pkg} is not installed.`,
69
+ "It is an optional dependency: --no-optional / --omit=optional skips it, or the install was interrupted.",
70
+ "Reinstall: npm install cornhsu-polymigrate"
57
71
  );
58
- process.exit(1);
59
72
  }
60
73
 
74
+ // Ctrl-C:主控台的 Ctrl-C 同時送給這個 node 行程與子行程(Windows 的主控台群組、Unix 的前景行程群組都是如此)。
75
+ // node 沒有 SIGINT 處理器的話會先被結束——Windows 上結束碼是 0xC000013A,子行程乾淨回的 130 傳不出去。
76
+ // 掛一個什麼都不做的處理器,讓 node 等子行程自己收尾,再原樣轉傳它的 exit code。
77
+ process.on("SIGINT", () => {});
78
+
61
79
  const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
62
80
 
63
81
  if (result.error) {
64
- console.error(`無法執行 ${binary}:${result.error.message}`);
65
- process.exit(1);
82
+ fail(`could not start ${binary}: ${result.error.message}.`);
66
83
  }
67
84
 
68
85
  // 被訊號中斷時 status 會是 null;轉成慣例的 128+signal,別讓 CI 誤判為成功
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cornhsu-polymigrate",
3
- "version": "2.3.1",
3
+ "version": "3.0.0",
4
4
  "description": "The i18n-first static-site migrator — the only one that pairs your multilingual pages automatically. Mirrors a legacy site into frontmatter Markdown, auto-matches language versions, and honestly lists what it could not pair.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -36,11 +36,11 @@
36
36
  "node": ">=18"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@cornhsu/polymigrate-win32-x64": "2.3.1",
40
- "@cornhsu/polymigrate-win32-arm64": "2.3.1",
41
- "@cornhsu/polymigrate-linux-x64": "2.3.1",
42
- "@cornhsu/polymigrate-linux-arm64": "2.3.1",
43
- "@cornhsu/polymigrate-darwin-x64": "2.3.1",
44
- "@cornhsu/polymigrate-darwin-arm64": "2.3.1"
39
+ "@cornhsu/polymigrate-win32-x64": "3.0.0",
40
+ "@cornhsu/polymigrate-win32-arm64": "3.0.0",
41
+ "@cornhsu/polymigrate-linux-x64": "3.0.0",
42
+ "@cornhsu/polymigrate-linux-arm64": "3.0.0",
43
+ "@cornhsu/polymigrate-darwin-x64": "3.0.0",
44
+ "@cornhsu/polymigrate-darwin-arm64": "3.0.0"
45
45
  }
46
46
  }