word-to-markdown 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/LICENSE +201 -0
- package/README.md +131 -0
- package/build/cli.js +40 -0
- package/build/cli.js.map +1 -0
- package/build/main.js +519 -0
- package/build/main.js.map +1 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright {yyyy} {name of copyright owner}
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Word to Markdown
|
|
2
|
+
|
|
3
|
+
Convert Word documents to beautiful Markdown. Via command line or in your browser. An even better version of the original [`word-to-markdown`](https://github.com/benbalter/word-to-markdown).
|
|
4
|
+
|
|
5
|
+
Try it in your browser at [word2md.com](https://word2md.com), or use it from the command line — no clone required:
|
|
6
|
+
|
|
7
|
+
```console
|
|
8
|
+
npx word-to-markdown input.docx > output.md
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Supports
|
|
12
|
+
|
|
13
|
+
- Paragraphs
|
|
14
|
+
- Numbered lists
|
|
15
|
+
- Bullet lists
|
|
16
|
+
- Nested Lists
|
|
17
|
+
- Headings
|
|
18
|
+
- Lists
|
|
19
|
+
- Tables
|
|
20
|
+
- Footnotes and endnotes
|
|
21
|
+
- Images
|
|
22
|
+
- Bold, italics, underlines, strikethrough, superscript and subscript.
|
|
23
|
+
- Links
|
|
24
|
+
- Line breaks
|
|
25
|
+
- Text boxes
|
|
26
|
+
- Comments
|
|
27
|
+
|
|
28
|
+
## How is this different from the original?
|
|
29
|
+
|
|
30
|
+
_TL;DR: This project is a complete rewrite, using modern tools and libraries, and is much faster and more reliable. The output should be the same or better. [Feedback welcome!](https://github.com/benbalter/word-to-markdown-js/issues/new)_
|
|
31
|
+
|
|
32
|
+
## A note on privacy
|
|
33
|
+
|
|
34
|
+
Word to Markdown is designed with privacy as a core principle. The application operates entirely client-side:
|
|
35
|
+
|
|
36
|
+
- **Complete client-side processing**: When using the web interface, all document conversion happens locally in your browser using JavaScript. Your documents never leave your computer.
|
|
37
|
+
- **No server uploads**: When using the web interface, files are processed entirely on your device. No document content is ever transmitted to any server.
|
|
38
|
+
- **No personal data collection**: The application does not collect, store, or transmit any personal information or document contents.
|
|
39
|
+
- **Privacy-first analytics**: The hosted version at word2md.com uses only privacy-centric Cloudflare Analytics for anonymous usage statistics. No Google Analytics or user tracking.
|
|
40
|
+
- **Self-hosting option**: For maximum privacy, you can run the application locally or self-host it without any analytics whatsoever.
|
|
41
|
+
|
|
42
|
+
Whether you use the command line tool, run it locally in your browser, or use the hosted version, your documents and privacy are protected.
|
|
43
|
+
|
|
44
|
+
## Command line
|
|
45
|
+
|
|
46
|
+
Run it directly with `npx` (downloads and runs the latest published version):
|
|
47
|
+
|
|
48
|
+
```console
|
|
49
|
+
npx word-to-markdown path/to/your/file.docx > output.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install it globally to get the `w2m` command:
|
|
53
|
+
|
|
54
|
+
```console
|
|
55
|
+
npm install -g word-to-markdown
|
|
56
|
+
w2m path/to/your/file.docx > output.md
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Use as a library
|
|
60
|
+
|
|
61
|
+
```console
|
|
62
|
+
npm install word-to-markdown
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
import convert, { convertWithWarnings } from 'word-to-markdown';
|
|
67
|
+
|
|
68
|
+
// Just the Markdown:
|
|
69
|
+
const markdown = await convert('path/to/your/file.docx');
|
|
70
|
+
|
|
71
|
+
// Markdown plus any document warnings (e.g. encryption, sensitivity labels):
|
|
72
|
+
const { markdown, warnings } = await convertWithWarnings(
|
|
73
|
+
'path/to/your/file.docx',
|
|
74
|
+
);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
In the browser, pass an `ArrayBuffer` instead of a file path.
|
|
78
|
+
|
|
79
|
+
## Running Locally
|
|
80
|
+
|
|
81
|
+
### Get set up
|
|
82
|
+
|
|
83
|
+
1. Clone the repo
|
|
84
|
+
2. Run `npm install`
|
|
85
|
+
|
|
86
|
+
### Command line (from source)
|
|
87
|
+
|
|
88
|
+
Run `npm run build:js` once, then `node build/cli.js path/to/your/file.docx`.
|
|
89
|
+
|
|
90
|
+
### Run the site locally
|
|
91
|
+
|
|
92
|
+
`npm run dev` starts the Astro dev server. To preview a production build, run
|
|
93
|
+
`npm run build` followed by `npm run preview`.
|
|
94
|
+
|
|
95
|
+
## Self-Hosting
|
|
96
|
+
|
|
97
|
+
To self-host the static site using Docker Compose:
|
|
98
|
+
|
|
99
|
+
1. Clone the repository
|
|
100
|
+
2. Run `npm install && npm run build`
|
|
101
|
+
3. Run `docker-compose up -d`
|
|
102
|
+
4. Access at http://localhost:3000
|
|
103
|
+
|
|
104
|
+
## More context
|
|
105
|
+
|
|
106
|
+
See the README of [the original Word to Markdown](https://github.com/benbalter/word-to-markdown?tab=readme-ov-file#the-problem) for the project's motivation.
|
|
107
|
+
|
|
108
|
+
### The old way
|
|
109
|
+
|
|
110
|
+
[The Original](https://github.com/benbalter/word-to-markdown) Word to Markdown is 10 years old. The conversion process was as follows:
|
|
111
|
+
|
|
112
|
+
1. Use [LibreOffice](https://www.libreoffice.org/) to convert the Word document to HTML.
|
|
113
|
+
2. Use a bunch of RegEx to clean up the HTML
|
|
114
|
+
3. User [Premailer](https://github.com/premailer/premailer) to inline the CSS
|
|
115
|
+
4. Use [Nokogiri](https://nokogiri.org) to manipulate the HTML further
|
|
116
|
+
5. Use [Reverse Markdown](https://github.com/xijo/reverse_markdown) to convert the HTML to Markdown
|
|
117
|
+
6. Use a bunch of RegEx to clean up the Markdown
|
|
118
|
+
|
|
119
|
+
Not only did this process require installing and shelling out to a huge binary (LibreOffice), but it was very fragile, and key projects like Reverse Markdown are no longer maintained. I tried experimenting with Pandoc, but it had many of the same limitation.
|
|
120
|
+
|
|
121
|
+
### The new way
|
|
122
|
+
|
|
123
|
+
1. Use [Mammoth.js](https://github.com/mwilliamson/mammoth.js/) to convert the Word document to HTML.
|
|
124
|
+
2. Use [Turndown](https://github.com/mixmark-io/turndown) to convert the HTML to Markdown.
|
|
125
|
+
3. Use [Markdownlint](https://github.com/DavidAnson/markdownlint) to clean up the Markdown.
|
|
126
|
+
|
|
127
|
+
All three of these projects are actively maintained and heavily used, and allows us to convert the document faster, and entirely in JavaScript. Heck, I think theoretically, this could run in the browser for added privacy.
|
|
128
|
+
|
|
129
|
+
It's still in beta, but so far, I've found the output to be better, with much less manual cleanup required. Notice something is off? Please [open an issue](https://github.com/benbalter/word-to-markdown-js/issues/new).
|
|
130
|
+
|
|
131
|
+
One note: This project does not yet attempt to guess heading levels based on font size. It could, but it's not yet implemented.
|
package/build/cli.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { __awaiter } from "tslib";
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { convertWithWarnings, UnsupportedFileError, FileNotFoundError, InvalidFileError, FilePermissionError, ConversionError, } from './main.js';
|
|
5
|
+
const program = new Command();
|
|
6
|
+
program.name('w2m');
|
|
7
|
+
program.description('Convert Word documents to beautiful Markdown');
|
|
8
|
+
program
|
|
9
|
+
.command('convert', { isDefault: true })
|
|
10
|
+
.argument('<file>', 'The Word document to convert')
|
|
11
|
+
.action((file) => __awaiter(void 0, void 0, void 0, function* () {
|
|
12
|
+
try {
|
|
13
|
+
const result = yield convertWithWarnings(file);
|
|
14
|
+
// Display warnings to stderr if any
|
|
15
|
+
if (result.warnings.length > 0) {
|
|
16
|
+
result.warnings.forEach((warning) => {
|
|
17
|
+
console.error(warning);
|
|
18
|
+
});
|
|
19
|
+
console.error(''); // Empty line for separation
|
|
20
|
+
}
|
|
21
|
+
// Output markdown to stdout
|
|
22
|
+
console.log(result.markdown);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
// Handle our custom errors with user-friendly messages
|
|
26
|
+
if (error instanceof UnsupportedFileError ||
|
|
27
|
+
error instanceof FileNotFoundError ||
|
|
28
|
+
error instanceof InvalidFileError ||
|
|
29
|
+
error instanceof FilePermissionError ||
|
|
30
|
+
error instanceof ConversionError) {
|
|
31
|
+
console.error(`Error: ${error.message}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// Handle unexpected errors (including non-Error objects)
|
|
35
|
+
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
program.parse();
|
|
40
|
+
//# sourceMappingURL=cli.js.map
|
package/build/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,GAChB,MAAM,WAAW,CAAC;AAEnB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACpB,OAAO,CAAC,WAAW,CAAC,8CAA8C,CAAC,CAAC;AACpE,OAAO;KACJ,OAAO,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;KACvC,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,CAAC;KAClD,MAAM,CAAC,CAAO,IAAI,EAAE,EAAE;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAE/C,oCAAoC;QACpC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAClC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,4BAA4B;QACjD,CAAC;QAED,4BAA4B;QAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,uDAAuD;QACvD,IACE,KAAK,YAAY,oBAAoB;YACrC,KAAK,YAAY,iBAAiB;YAClC,KAAK,YAAY,gBAAgB;YACjC,KAAK,YAAY,mBAAmB;YACpC,KAAK,YAAY,eAAe,EAChC,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,yDAAyD;QACzD,OAAO,CAAC,KAAK,CACX,QAAQ,EACR,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAA,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/build/main.js
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import TurndownService from '@joplin/turndown';
|
|
3
|
+
import * as turndownPluginGfm from '@joplin/turndown-plugin-gfm';
|
|
4
|
+
import * as mammoth from 'mammoth';
|
|
5
|
+
import * as markdownlint from 'markdownlint/sync';
|
|
6
|
+
import { applyFixes } from 'markdownlint';
|
|
7
|
+
import { parse } from 'node-html-parser';
|
|
8
|
+
import * as prettier from 'prettier';
|
|
9
|
+
import * as prettierMarkdown from 'prettier/plugins/markdown';
|
|
10
|
+
import JSZip from 'jszip';
|
|
11
|
+
import fs from 'fs/promises';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
// Custom error class for unsupported file formats
|
|
14
|
+
export class UnsupportedFileError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'UnsupportedFileError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// Custom error class for file not found
|
|
21
|
+
export class FileNotFoundError extends Error {
|
|
22
|
+
constructor(filePath) {
|
|
23
|
+
const location = filePath ? `: "${filePath}"` : '';
|
|
24
|
+
super(`File not found${location}. Please check that the file exists and the path is correct.`);
|
|
25
|
+
this.name = 'FileNotFoundError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Custom error class for invalid/corrupted files
|
|
29
|
+
export class InvalidFileError extends Error {
|
|
30
|
+
constructor(filePath) {
|
|
31
|
+
const location = filePath ? `: "${filePath}"` : '';
|
|
32
|
+
super(`Invalid file${location}. The file is not a valid .docx file or is corrupted. Please ensure the file is a valid Microsoft Word document (.docx format).`);
|
|
33
|
+
this.name = 'InvalidFileError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Custom error class for permission errors
|
|
37
|
+
export class FilePermissionError extends Error {
|
|
38
|
+
constructor(filePath) {
|
|
39
|
+
const location = filePath ? `: "${filePath}"` : '';
|
|
40
|
+
super(`Permission denied${location}. Cannot read the file. Please check file permissions.`);
|
|
41
|
+
this.name = 'FilePermissionError';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Custom error class for general conversion errors
|
|
45
|
+
export class ConversionError extends Error {
|
|
46
|
+
constructor(message, originalError) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = 'ConversionError';
|
|
49
|
+
// Capture stack trace if available (Node.js/V8 specific)
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
const ErrorWithCapture = Error;
|
|
52
|
+
if (typeof ErrorWithCapture.captureStackTrace === 'function') {
|
|
53
|
+
ErrorWithCapture.captureStackTrace(this, this.constructor);
|
|
54
|
+
}
|
|
55
|
+
if (originalError) {
|
|
56
|
+
// Use standard error chaining for better debugging tool support
|
|
57
|
+
this.cause = originalError;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const defaultTurndownOptions = {
|
|
62
|
+
headingStyle: 'atx',
|
|
63
|
+
codeBlockStyle: 'fenced',
|
|
64
|
+
bulletListMarker: '-',
|
|
65
|
+
};
|
|
66
|
+
// Check if a file path has a .doc extension (unsupported format)
|
|
67
|
+
export function validateFileExtension(filePath) {
|
|
68
|
+
// Use manual extension parsing (works in both Node.js and browser)
|
|
69
|
+
const filename = filePath.toLowerCase();
|
|
70
|
+
const lastDotIndex = filename.lastIndexOf('.');
|
|
71
|
+
const ext = lastDotIndex !== -1 ? filename.substring(lastDotIndex) : '';
|
|
72
|
+
if (ext === '.doc') {
|
|
73
|
+
throw new UnsupportedFileError('This tool only supports .docx files, not .doc files. Please save your document as a .docx file and try again.');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Validates that a file path is safe to use (Node.js only)
|
|
77
|
+
// Prevents path traversal attacks by checking for parent directory references
|
|
78
|
+
function validateFilePath(filePath) {
|
|
79
|
+
// Check for path traversal attempts
|
|
80
|
+
if (filePath.includes('..')) {
|
|
81
|
+
throw new Error('Invalid file path: path traversal not allowed');
|
|
82
|
+
}
|
|
83
|
+
// Resolve to absolute path to check for traversal
|
|
84
|
+
// Note: This uses the path module which is Node.js-only, but this function
|
|
85
|
+
// is only called in Node.js contexts (CLI, server, direct API use with file paths)
|
|
86
|
+
const resolvedPath = path.resolve(filePath);
|
|
87
|
+
// Check for absolute paths to dangerous system directories (Unix-like systems)
|
|
88
|
+
const dangerousPaths = ['/etc/', '/sys/', '/proc/', '/root/', '/boot/'];
|
|
89
|
+
for (const dangerousPath of dangerousPaths) {
|
|
90
|
+
if (resolvedPath.startsWith(dangerousPath)) {
|
|
91
|
+
throw new Error('Invalid file path: access to system directories not allowed');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Check for Windows system directories
|
|
95
|
+
const windowsDangerousPaths = ['C:\\Windows\\', 'C:\\Program Files\\'];
|
|
96
|
+
for (const dangerousPath of windowsDangerousPaths) {
|
|
97
|
+
if (resolvedPath.toUpperCase().startsWith(dangerousPath.toUpperCase())) {
|
|
98
|
+
throw new Error('Invalid file path: access to system directories not allowed');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Map of common HTML entities to decode
|
|
103
|
+
const decodeMap = {
|
|
104
|
+
'&': '&',
|
|
105
|
+
// Don't decode < and > in our custom decoder
|
|
106
|
+
// Let Turndown handle them appropriately based on context
|
|
107
|
+
'"': '"',
|
|
108
|
+
''': "'",
|
|
109
|
+
''': "'",
|
|
110
|
+
''': "'",
|
|
111
|
+
' ': ' ',
|
|
112
|
+
'©': '©',
|
|
113
|
+
'®': '®',
|
|
114
|
+
'™': '™',
|
|
115
|
+
'…': '…',
|
|
116
|
+
'—': '—',
|
|
117
|
+
'–': '–',
|
|
118
|
+
'‘': '\u2018',
|
|
119
|
+
'’': '\u2019',
|
|
120
|
+
'“': '\u201C',
|
|
121
|
+
'”': '\u201D',
|
|
122
|
+
};
|
|
123
|
+
// Maximum iterations for decoding nested HTML entities to prevent infinite loops
|
|
124
|
+
const MAX_DECODE_ITERATIONS = 10;
|
|
125
|
+
// Decode HTML entities in text content
|
|
126
|
+
function decodeHtmlEntities(html) {
|
|
127
|
+
function decodeOnce(text) {
|
|
128
|
+
// Use a more specific regex pattern to avoid catastrophic backtracking
|
|
129
|
+
// Match: & followed by either:
|
|
130
|
+
// - a-zA-Z letters (for named entities like &, , etc.)
|
|
131
|
+
// - # followed by digits (for numeric entities like ©)
|
|
132
|
+
// - #[xX] followed by hex digits (for hex entities like ' or ')
|
|
133
|
+
// All terminated with a semicolon
|
|
134
|
+
return text.replace(/&(?:[a-zA-Z]+|#\d+|#[xX][0-9a-fA-F]+);/g, (entity) => {
|
|
135
|
+
// Handle named entities
|
|
136
|
+
if (decodeMap[entity]) {
|
|
137
|
+
return decodeMap[entity];
|
|
138
|
+
}
|
|
139
|
+
// Handle numeric entities {
|
|
140
|
+
const numericMatch = entity.match(/^&#(\d+);$/);
|
|
141
|
+
if (numericMatch) {
|
|
142
|
+
return String.fromCharCode(parseInt(numericMatch[1], 10));
|
|
143
|
+
}
|
|
144
|
+
// Handle hex entities 
|
|
145
|
+
const hexMatch = entity.match(/^&#x([0-9a-fA-F]+);$/i);
|
|
146
|
+
if (hexMatch) {
|
|
147
|
+
return String.fromCharCode(parseInt(hexMatch[1], 16));
|
|
148
|
+
}
|
|
149
|
+
// Return original if not recognized
|
|
150
|
+
return entity;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Keep decoding until no more entities are found (handles double/triple encoding)
|
|
154
|
+
let decoded = html;
|
|
155
|
+
let prevDecoded;
|
|
156
|
+
let iterations = 0;
|
|
157
|
+
do {
|
|
158
|
+
prevDecoded = decoded;
|
|
159
|
+
decoded = decodeOnce(decoded);
|
|
160
|
+
iterations++;
|
|
161
|
+
} while (decoded !== prevDecoded &&
|
|
162
|
+
decoded.includes('&') &&
|
|
163
|
+
iterations < MAX_DECODE_ITERATIONS);
|
|
164
|
+
return decoded;
|
|
165
|
+
}
|
|
166
|
+
// Turndown will add an empty header if the first row
|
|
167
|
+
// of the table isn't `<th>` elements. This function
|
|
168
|
+
// converts the first row of a table to `<th>` elements
|
|
169
|
+
// so that it renders correctly in Markdown.
|
|
170
|
+
// Common unicode bullets that might appear in Word documents - compiled once
|
|
171
|
+
const unicodeBullets = ['•', '◦', '▪', '▫', '‣', '⁃', '∙', '·'];
|
|
172
|
+
const bulletRegex = new RegExp(`^\\s*[${unicodeBullets.map((b) => b.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('')}]\\s*`);
|
|
173
|
+
// Process HTML in a single pass: convert table headers and remove unicode bullets
|
|
174
|
+
// This is more efficient than parsing the HTML twice
|
|
175
|
+
function processHtml(html) {
|
|
176
|
+
const root = parse(html);
|
|
177
|
+
// Process tables - convert first row to table headers
|
|
178
|
+
root.querySelectorAll('table').forEach((table) => {
|
|
179
|
+
const firstRow = table.querySelector('tr');
|
|
180
|
+
if (!firstRow)
|
|
181
|
+
return;
|
|
182
|
+
// If first row already has TH elements, leave it alone
|
|
183
|
+
if (firstRow.querySelector('th'))
|
|
184
|
+
return;
|
|
185
|
+
// Check if first row is empty or has only empty cells
|
|
186
|
+
const cells = firstRow.querySelectorAll('td');
|
|
187
|
+
const isEmpty = cells.length === 0 || cells.every((cell) => { var _a; return !((_a = cell.textContent) === null || _a === void 0 ? void 0 : _a.trim()); });
|
|
188
|
+
if (isEmpty) {
|
|
189
|
+
// Remove empty first row and find the first non-empty row to convert
|
|
190
|
+
firstRow.remove();
|
|
191
|
+
const nextRow = table.querySelector('tr');
|
|
192
|
+
if (nextRow) {
|
|
193
|
+
nextRow.querySelectorAll('td').forEach((cell) => {
|
|
194
|
+
cell.tagName = 'th';
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
// Convert first row TD elements to TH
|
|
200
|
+
cells.forEach((cell) => {
|
|
201
|
+
cell.tagName = 'th';
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
// Process lists - remove unicode bullets from unnumbered list items
|
|
206
|
+
root.querySelectorAll('ul li').forEach((listItem) => {
|
|
207
|
+
// Get the text content and remove unicode bullets from the beginning
|
|
208
|
+
const textContent = listItem.innerHTML;
|
|
209
|
+
const cleanedContent = textContent.replace(bulletRegex, '');
|
|
210
|
+
if (cleanedContent !== textContent) {
|
|
211
|
+
listItem.innerHTML = cleanedContent;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return root.toString();
|
|
215
|
+
}
|
|
216
|
+
// Reusable TurndownService instance to avoid recreating it for each conversion
|
|
217
|
+
let turndownServiceInstance = null;
|
|
218
|
+
function getTurndownService(options = {}) {
|
|
219
|
+
// Create a new instance if options are provided, otherwise reuse the singleton
|
|
220
|
+
if (Object.keys(options).length > 0) {
|
|
221
|
+
const service = new TurndownService(Object.assign(Object.assign({}, options), defaultTurndownOptions));
|
|
222
|
+
service.use(turndownPluginGfm.gfm);
|
|
223
|
+
return service;
|
|
224
|
+
}
|
|
225
|
+
if (!turndownServiceInstance) {
|
|
226
|
+
turndownServiceInstance = new TurndownService(defaultTurndownOptions);
|
|
227
|
+
turndownServiceInstance.use(turndownPluginGfm.gfm);
|
|
228
|
+
}
|
|
229
|
+
return turndownServiceInstance;
|
|
230
|
+
}
|
|
231
|
+
// Convert HTML to GitHub-flavored Markdown
|
|
232
|
+
export function htmlToMd(html, options = {}) {
|
|
233
|
+
// Decode HTML entities before conversion
|
|
234
|
+
const decodedHtml = decodeHtmlEntities(html);
|
|
235
|
+
const turndownService = getTurndownService(options);
|
|
236
|
+
return turndownService.turndown(decodedHtml).trim();
|
|
237
|
+
}
|
|
238
|
+
// Pre-compiled regex patterns for better performance
|
|
239
|
+
const numberedListRegex = /^(\s*)(\d+)\.\s/gm;
|
|
240
|
+
const nonBreakingSpacesRegex = /[\u00A0\u2007\u202F\u2060\uFEFF]/g;
|
|
241
|
+
const smartQuotesRegex = /[\u201C\u201D\u2018\u2019\u2013\u2014]/g;
|
|
242
|
+
// Map for non-breaking space replacements
|
|
243
|
+
const nonBreakingSpaceMap = {
|
|
244
|
+
'\u00A0': ' ', // Non-breaking space
|
|
245
|
+
'\u2007': ' ', // Figure space
|
|
246
|
+
'\u202F': ' ', // Narrow no-break space
|
|
247
|
+
'\u2060': '', // Word joiner (zero-width non-breaking space)
|
|
248
|
+
'\uFEFF': '', // Zero-width no-break space (BOM)
|
|
249
|
+
};
|
|
250
|
+
// Map for smart quote replacements
|
|
251
|
+
const smartQuoteMap = {
|
|
252
|
+
'\u201C': '"', // Left double quotation mark
|
|
253
|
+
'\u201D': '"', // Right double quotation mark
|
|
254
|
+
'\u2018': "'", // Left single quotation mark
|
|
255
|
+
'\u2019': "'", // Right single quotation mark
|
|
256
|
+
'\u2013': '-', // En dash
|
|
257
|
+
'\u2014': '-', // Em dash
|
|
258
|
+
};
|
|
259
|
+
// Convert numbered lists to bullet lists
|
|
260
|
+
function convertNumberedListsToBullets(md) {
|
|
261
|
+
// Replace numbered list items with bullet list items
|
|
262
|
+
// This regex matches lines that start with optional whitespace, a number, a dot, and a space
|
|
263
|
+
return md.replace(numberedListRegex, '$1- ');
|
|
264
|
+
}
|
|
265
|
+
// Remove unicode non-breaking spaces and convert smart quotes to ASCII in a single pass
|
|
266
|
+
function normalizeText(md) {
|
|
267
|
+
return md
|
|
268
|
+
.replace(nonBreakingSpacesRegex, (char) => nonBreakingSpaceMap[char])
|
|
269
|
+
.replace(smartQuotesRegex, (char) => smartQuoteMap[char]);
|
|
270
|
+
}
|
|
271
|
+
// Lint the Markdown and correct any issues
|
|
272
|
+
function lint(md) {
|
|
273
|
+
const lintResult = markdownlint.lint({ strings: { md } });
|
|
274
|
+
return applyFixes(md, lintResult['md']).trim();
|
|
275
|
+
}
|
|
276
|
+
// Format the Markdown with Prettier
|
|
277
|
+
function prettify(md) {
|
|
278
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
279
|
+
const formatted = yield prettier.format(md, {
|
|
280
|
+
parser: 'markdown',
|
|
281
|
+
plugins: [prettierMarkdown],
|
|
282
|
+
});
|
|
283
|
+
return formatted.trim();
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
// Extract document properties from a .docx file
|
|
287
|
+
export function extractDocumentProperties(input) {
|
|
288
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
289
|
+
const properties = {};
|
|
290
|
+
try {
|
|
291
|
+
let arrayBuffer;
|
|
292
|
+
if (typeof input === 'string') {
|
|
293
|
+
// Validate the file path to prevent path traversal attacks
|
|
294
|
+
validateFilePath(input);
|
|
295
|
+
// Read file from path and convert to ArrayBuffer
|
|
296
|
+
const fileBuffer = yield fs.readFile(input);
|
|
297
|
+
const slicedBuffer = fileBuffer.buffer.slice(fileBuffer.byteOffset, fileBuffer.byteOffset + fileBuffer.byteLength);
|
|
298
|
+
// Ensure we have an ArrayBuffer (not SharedArrayBuffer)
|
|
299
|
+
// In practice, Node.js Buffer.buffer returns an ArrayBuffer, but TypeScript
|
|
300
|
+
// can't guarantee this, so we handle both cases
|
|
301
|
+
if (slicedBuffer instanceof ArrayBuffer) {
|
|
302
|
+
arrayBuffer = slicedBuffer;
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
// Convert SharedArrayBuffer to ArrayBuffer by copying the data
|
|
306
|
+
const uint8Array = new Uint8Array(slicedBuffer);
|
|
307
|
+
const newArrayBuffer = new ArrayBuffer(uint8Array.byteLength);
|
|
308
|
+
new Uint8Array(newArrayBuffer).set(uint8Array);
|
|
309
|
+
arrayBuffer = newArrayBuffer;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
arrayBuffer = input;
|
|
314
|
+
}
|
|
315
|
+
const zip = yield JSZip.loadAsync(arrayBuffer);
|
|
316
|
+
// Check for encryption - encrypted files have EncryptionInfo and EncryptedPackage
|
|
317
|
+
const encryptionInfo = zip.file('EncryptionInfo');
|
|
318
|
+
if (encryptionInfo) {
|
|
319
|
+
properties.encryption = true;
|
|
320
|
+
}
|
|
321
|
+
// Try to read core properties
|
|
322
|
+
const corePropsFile = zip.file('docProps/core.xml');
|
|
323
|
+
if (corePropsFile) {
|
|
324
|
+
const coreXml = yield corePropsFile.async('string');
|
|
325
|
+
// Look for keywords that might indicate sensitivity/confidentiality
|
|
326
|
+
if (coreXml.toLowerCase().includes('confidential') ||
|
|
327
|
+
coreXml.toLowerCase().includes('sensitive')) {
|
|
328
|
+
properties.confidentiality = 'detected in core properties';
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// Try to read custom properties
|
|
332
|
+
const customPropsFile = zip.file('docProps/custom.xml');
|
|
333
|
+
if (customPropsFile) {
|
|
334
|
+
const customXml = yield customPropsFile.async('string');
|
|
335
|
+
const customXmlLower = customXml.toLowerCase();
|
|
336
|
+
// Use regex patterns to detect sensitivity/confidentiality properties
|
|
337
|
+
// We use regex instead of full XML parsing for performance and simplicity,
|
|
338
|
+
// as we only need to detect the presence of specific property names, not extract values
|
|
339
|
+
// Pattern matches common sensitivity/confidentiality property names:
|
|
340
|
+
// - "Sensitivity" (standard Office property)
|
|
341
|
+
// - "MSIP_Label_*" (Microsoft Information Protection labels)
|
|
342
|
+
// - Any property with "confidential" or "sensitive" in the name
|
|
343
|
+
// We only check for the property name attribute existence, not the full element content,
|
|
344
|
+
// to avoid potential catastrophic backtracking on large/malformed XML
|
|
345
|
+
const sensitivityPattern = /<property[^>]*\bname="(?:Sensitivity|MSIP_Label_[^"]*|[^"]*(?:confidential|sensitive)[^"]*)"[^>]*>/gi;
|
|
346
|
+
const hasSensitivityProperty = sensitivityPattern.test(customXml);
|
|
347
|
+
const hasConfidentialText = customXmlLower.includes('confidential');
|
|
348
|
+
const hasMSIPLabel = customXmlLower.includes('msip_label');
|
|
349
|
+
if (hasSensitivityProperty || hasMSIPLabel) {
|
|
350
|
+
properties.sensitivity = 'detected in custom properties';
|
|
351
|
+
}
|
|
352
|
+
if (hasConfidentialText) {
|
|
353
|
+
properties.confidentiality = 'detected in custom properties';
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Check for document protection
|
|
357
|
+
const settingsFile = zip.file('word/settings.xml');
|
|
358
|
+
if (settingsFile) {
|
|
359
|
+
const settingsXml = yield settingsFile.async('string');
|
|
360
|
+
if (settingsXml.includes('<w:documentProtection') ||
|
|
361
|
+
settingsXml.includes('<w:writeProtection')) {
|
|
362
|
+
properties.protection = true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
// If we can't extract properties, just continue without them
|
|
368
|
+
// This might happen with encrypted, corrupted, or non-standard .docx files
|
|
369
|
+
// We log the error in development mode but don't fail the conversion
|
|
370
|
+
if (process.env.NODE_ENV === 'development') {
|
|
371
|
+
console.warn('Failed to extract document properties:', error);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return properties;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
// Generate warnings based on document properties
|
|
378
|
+
export function generateWarnings(properties) {
|
|
379
|
+
const warnings = [];
|
|
380
|
+
if (properties.encryption) {
|
|
381
|
+
warnings.push('Warning: This document appears to be encrypted. Conversion may not include all content or may fail entirely.');
|
|
382
|
+
}
|
|
383
|
+
if (properties.sensitivity) {
|
|
384
|
+
warnings.push(`Warning: This document has sensitivity labels (${properties.sensitivity}). Please ensure you have permission to convert and share this content.`);
|
|
385
|
+
}
|
|
386
|
+
if (properties.confidentiality) {
|
|
387
|
+
warnings.push(`Warning: This document contains confidentiality markers (${properties.confidentiality}). Please verify that conversion is authorized.`);
|
|
388
|
+
}
|
|
389
|
+
if (properties.protection) {
|
|
390
|
+
warnings.push('Warning: This document has editing restrictions enabled. Some content may not convert properly.');
|
|
391
|
+
}
|
|
392
|
+
return warnings;
|
|
393
|
+
}
|
|
394
|
+
// Converts a Word document to crisp, clean Markdown with warnings
|
|
395
|
+
export function convertWithWarnings(input_1) {
|
|
396
|
+
return __awaiter(this, arguments, void 0, function* (input, options = {}) {
|
|
397
|
+
// Normalize input so that the underlying .docx content is read at most once
|
|
398
|
+
let mammothInput;
|
|
399
|
+
let propertiesInput = input;
|
|
400
|
+
if (typeof input === 'string') {
|
|
401
|
+
// Validate file extension for file path inputs
|
|
402
|
+
validateFileExtension(input);
|
|
403
|
+
// Validate the file path to prevent path traversal attacks
|
|
404
|
+
validateFilePath(input);
|
|
405
|
+
// Read the file once and share the buffer between
|
|
406
|
+
// property extraction and Mammoth conversion to avoid
|
|
407
|
+
// redundant disk reads for large documents
|
|
408
|
+
const fileBuffer = yield fs.readFile(input);
|
|
409
|
+
const slicedBuffer = fileBuffer.buffer.slice(fileBuffer.byteOffset, fileBuffer.byteOffset + fileBuffer.byteLength);
|
|
410
|
+
// Ensure we have an ArrayBuffer (not SharedArrayBuffer) for property extraction
|
|
411
|
+
let arrayBuffer;
|
|
412
|
+
if (slicedBuffer instanceof ArrayBuffer) {
|
|
413
|
+
arrayBuffer = slicedBuffer;
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
// Convert SharedArrayBuffer to ArrayBuffer by copying the data
|
|
417
|
+
const uint8Array = new Uint8Array(slicedBuffer);
|
|
418
|
+
const newArrayBuffer = new ArrayBuffer(uint8Array.byteLength);
|
|
419
|
+
new Uint8Array(newArrayBuffer).set(uint8Array);
|
|
420
|
+
arrayBuffer = newArrayBuffer;
|
|
421
|
+
}
|
|
422
|
+
propertiesInput = arrayBuffer;
|
|
423
|
+
mammothInput = { buffer: fileBuffer };
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
propertiesInput = input;
|
|
427
|
+
// In Node.js, mammoth expects { buffer }, in browser it expects { arrayBuffer }
|
|
428
|
+
// Check for Buffer availability to determine the environment
|
|
429
|
+
if (typeof Buffer !== 'undefined') {
|
|
430
|
+
mammothInput = { buffer: Buffer.from(input) };
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
mammothInput = { arrayBuffer: input };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// Extract document properties to check for confidentiality flags
|
|
437
|
+
const properties = yield extractDocumentProperties(propertiesInput);
|
|
438
|
+
const warnings = generateWarnings(properties);
|
|
439
|
+
const mammothResult = yield mammoth.convertToHtml(mammothInput, options.mammoth);
|
|
440
|
+
const processedHtml = processHtml(mammothResult.value);
|
|
441
|
+
const md = htmlToMd(processedHtml, options.turndown);
|
|
442
|
+
const mdWithBullets = convertNumberedListsToBullets(md);
|
|
443
|
+
const normalizedMd = normalizeText(mdWithBullets);
|
|
444
|
+
const cleanedMd = lint(normalizedMd);
|
|
445
|
+
const formattedMd = yield prettify(cleanedMd);
|
|
446
|
+
return {
|
|
447
|
+
markdown: formattedMd,
|
|
448
|
+
warnings,
|
|
449
|
+
};
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
// Converts a Word document to crisp, clean Markdown
|
|
453
|
+
export default function convert(input_1) {
|
|
454
|
+
return __awaiter(this, arguments, void 0, function* (input, options = {}) {
|
|
455
|
+
let inputObj;
|
|
456
|
+
let filePath;
|
|
457
|
+
try {
|
|
458
|
+
if (typeof input === 'string') {
|
|
459
|
+
filePath = input;
|
|
460
|
+
// Validate file extension for file path inputs
|
|
461
|
+
validateFileExtension(input);
|
|
462
|
+
inputObj = { path: input };
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
inputObj = { arrayBuffer: input };
|
|
466
|
+
}
|
|
467
|
+
const mammothResult = yield mammoth.convertToHtml(inputObj, options.mammoth);
|
|
468
|
+
const processedHtml = processHtml(mammothResult.value);
|
|
469
|
+
const md = htmlToMd(processedHtml, options.turndown);
|
|
470
|
+
const mdWithBullets = convertNumberedListsToBullets(md);
|
|
471
|
+
const normalizedMd = normalizeText(mdWithBullets);
|
|
472
|
+
const cleanedMd = lint(normalizedMd);
|
|
473
|
+
const formattedMd = yield prettify(cleanedMd);
|
|
474
|
+
return formattedMd;
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
// Re-throw our custom errors as-is
|
|
478
|
+
if (error instanceof UnsupportedFileError ||
|
|
479
|
+
error instanceof FileNotFoundError ||
|
|
480
|
+
error instanceof InvalidFileError ||
|
|
481
|
+
error instanceof FilePermissionError ||
|
|
482
|
+
error instanceof ConversionError) {
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
// Handle specific error types from underlying libraries
|
|
486
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
487
|
+
const errorCode = error && typeof error === 'object' && 'code' in error
|
|
488
|
+
? error.code
|
|
489
|
+
: undefined;
|
|
490
|
+
// File not found errors (only occur with file path inputs)
|
|
491
|
+
if (errorCode === 'ENOENT') {
|
|
492
|
+
throw new FileNotFoundError(filePath);
|
|
493
|
+
}
|
|
494
|
+
// Permission errors (only occur with file path inputs)
|
|
495
|
+
if (errorCode === 'EACCES' || errorCode === 'EPERM') {
|
|
496
|
+
throw new FilePermissionError(filePath);
|
|
497
|
+
}
|
|
498
|
+
// Invalid .docx file errors (from JSZip or mammoth during file parsing)
|
|
499
|
+
// These patterns typically come from JSZip/mammoth when parsing the file structure.
|
|
500
|
+
// While there's a small theoretical risk of matching document content in unusual
|
|
501
|
+
// scenarios (e.g., embedded error messages), these specific technical phrases are
|
|
502
|
+
// highly unlikely to appear in normal document content.
|
|
503
|
+
if (errorMessage.includes('end of central directory') || // JSZip: invalid ZIP structure
|
|
504
|
+
errorMessage.includes('zip file') || // JSZip: not a valid ZIP
|
|
505
|
+
errorMessage.includes('Corrupted zip') || // JSZip: corrupted ZIP file
|
|
506
|
+
errorMessage.includes('End of data reached') || // JSZip: truncated file
|
|
507
|
+
errorMessage.includes('Could not find file') // mammoth: missing required file in .docx
|
|
508
|
+
) {
|
|
509
|
+
// Note: For ArrayBuffer inputs (e.g., web uploads), filePath will be undefined,
|
|
510
|
+
// so the error message won't include the original filename. The web interface
|
|
511
|
+
// could be enhanced to pass the filename separately if needed.
|
|
512
|
+
throw new InvalidFileError(filePath);
|
|
513
|
+
}
|
|
514
|
+
// Wrap other errors with a general conversion error
|
|
515
|
+
throw new ConversionError('An error occurred while converting the document. Please ensure the file is a valid .docx file and try again.', error instanceof Error ? error : undefined);
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "word-to-markdown",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Convert Word documents to beautiful Markdown.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"word",
|
|
8
|
+
"docx",
|
|
9
|
+
"markdown",
|
|
10
|
+
"convert",
|
|
11
|
+
"converter",
|
|
12
|
+
"mammoth",
|
|
13
|
+
"turndown",
|
|
14
|
+
"cli"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://word2md.com",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/benbalter/word-to-markdown-js.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/benbalter/word-to-markdown-js/issues"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">= 22.13"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@astrojs/check": "^0.9.9",
|
|
29
|
+
"@astrojs/sitemap": "^3.7.3",
|
|
30
|
+
"@eslint/js": "^9.39.4",
|
|
31
|
+
"@nuasite/checks": "^0.46.1",
|
|
32
|
+
"@playwright/test": "^1.61.0",
|
|
33
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
34
|
+
"@tailwindcss/vite": "^4.3.1",
|
|
35
|
+
"@types/jest": "~30.0",
|
|
36
|
+
"@types/node": "~26",
|
|
37
|
+
"@types/turndown": "^5.0.6",
|
|
38
|
+
"astro": "^7.0.0",
|
|
39
|
+
"eslint": "^9.39.4",
|
|
40
|
+
"eslint-config-prettier": "~10.1",
|
|
41
|
+
"eslint-plugin-jest": "~29.15",
|
|
42
|
+
"globals": "^17.7.0",
|
|
43
|
+
"jest": "~30.4",
|
|
44
|
+
"jest-util": "~30.4",
|
|
45
|
+
"playwright": "^1.61.0",
|
|
46
|
+
"tailwindcss": "^4.3.1",
|
|
47
|
+
"ts-api-utils": "~2.5",
|
|
48
|
+
"ts-jest": "~29.4",
|
|
49
|
+
"typescript": "^6.0.3",
|
|
50
|
+
"typescript-eslint": "^8.62.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"fix": "eslint --fix . && prettier --write .",
|
|
54
|
+
"dev": "astro dev",
|
|
55
|
+
"build": "npm run build:js && npm run build:site",
|
|
56
|
+
"build:js": "tsc",
|
|
57
|
+
"build:site": "astro check --minimumSeverity error && astro build",
|
|
58
|
+
"preview": "astro preview",
|
|
59
|
+
"lint": "eslint . && prettier --check .",
|
|
60
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
61
|
+
"test:e2e": "playwright test",
|
|
62
|
+
"test:e2e:headed": "playwright test --headed",
|
|
63
|
+
"test:e2e:debug": "playwright test --debug",
|
|
64
|
+
"all": "npm run fix && npm run test && npm run build && npm run check-builds",
|
|
65
|
+
"check-builds": "./scripts/check-builds.sh",
|
|
66
|
+
"prepack": "rm -rf build && npm run build:js"
|
|
67
|
+
},
|
|
68
|
+
"author": "Ben Balter <ben@balter.com>",
|
|
69
|
+
"license": "ISC",
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@joplin/turndown": "^4.0.85",
|
|
72
|
+
"@joplin/turndown-plugin-gfm": "^1.0.67",
|
|
73
|
+
"clipboard": "^2.0.11",
|
|
74
|
+
"commander": "^15.0.0",
|
|
75
|
+
"jszip": "^3.10.1",
|
|
76
|
+
"mammoth": "^1.12.0",
|
|
77
|
+
"markdownlint": "^0.41.0",
|
|
78
|
+
"node-html-parser": "^7.0.1",
|
|
79
|
+
"prettier": "~3.8",
|
|
80
|
+
"rehype-sanitize": "^6.0.0",
|
|
81
|
+
"rehype-stringify": "^10.0.1",
|
|
82
|
+
"remark-gfm": "^4.0.1",
|
|
83
|
+
"remark-parse": "^11.0.0",
|
|
84
|
+
"remark-rehype": "^11.1.2",
|
|
85
|
+
"tslib": "~2.8",
|
|
86
|
+
"unified": "^11.0.5",
|
|
87
|
+
"url": "^0.11.4"
|
|
88
|
+
},
|
|
89
|
+
"volta": {
|
|
90
|
+
"node": "22.13.0"
|
|
91
|
+
},
|
|
92
|
+
"bin": {
|
|
93
|
+
"w2m": "build/cli.js",
|
|
94
|
+
"word-to-markdown": "build/cli.js"
|
|
95
|
+
},
|
|
96
|
+
"main": "build/main.js",
|
|
97
|
+
"files": [
|
|
98
|
+
"build/cli.js",
|
|
99
|
+
"build/cli.js.map",
|
|
100
|
+
"build/main.js",
|
|
101
|
+
"build/main.js.map"
|
|
102
|
+
]
|
|
103
|
+
}
|