Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
3eb98533ae | |||
f5f021834e | |||
35702c3f77 | |||
8fbbd571ad | |||
2dd228be69 | |||
562835cfe7 | |||
6bac06e84d | |||
88436168ee | |||
0c3206d2e2 | |||
033debd339 | |||
d4f160732a | |||
4238fc9ab4 | |||
0d042b8dd1 | |||
afdd5f2d8f | |||
70c7b3d1b3 | |||
7b1cd5fe86 | |||
09ab169ffd | |||
35a82cf12e | |||
8d33cb01dd | |||
438d11dd86 | |||
d23b0d0789 | |||
1fa8c7ac82 | |||
3d6ead0359 | |||
379050dccd | |||
f9d1c197cf | |||
a89ee463e6 | |||
036caaacc8 | |||
80c37afe2b | |||
3a36d912fe | |||
c741d5d8ac | |||
0fa7dd13ad | |||
d5d80550e7 | |||
23a0a59454 | |||
066aa56f6a | |||
3997b7a5d6 | |||
ccc391607a | |||
9577192d41 | |||
968c3aed0f | |||
9c7a80729b | |||
5f26ca1c94 | |||
f2b1587bff | |||
3b9f80fb1a | |||
8da57bec51 | |||
63db9f290c | |||
e8bd4bd6c6 | |||
28d93b0c42 | |||
95f8e37006 | |||
87083cb264 | |||
1e8dc95c9c | |||
abfa242c28 | |||
56b5414152 | |||
8f46060be2 | |||
93b17ca2af | |||
66008115b8 | |||
55697a7c32 | |||
e9b259db6b | |||
55c51f7af1 | |||
4e7fd6a6dc | |||
6294ec448f | |||
668198b5d1 | |||
56b2ffe706 | |||
2ab06ffe36 | |||
0c51082bb9 | |||
9a9a2019f6 | |||
d94348f5ca | |||
4c10741862 | |||
222a25e4e2 | |||
5fd4490c4f | |||
e1cc65cb97 | |||
991f837bc1 | |||
87b3035465 | |||
4498305a6c | |||
cac7abba91 |
@ -12,7 +12,6 @@
|
|||||||
[![Downloads per month][downloads-image]][downloads-url]
|
[![Downloads per month][downloads-image]][downloads-url]
|
||||||
[![GitHub Action Workflow Status][github-actions-workflow-image]][github-actions-workflow-url]
|
[![GitHub Action Workflow Status][github-actions-workflow-image]][github-actions-workflow-url]
|
||||||
[![Known Vulnerabilities][snky-image]][snky-url]
|
[![Known Vulnerabilities][snky-image]][snky-url]
|
||||||
[![Chat on Gitter][gitter-image]][gitter-url]
|
|
||||||
[![PRs Welcome][pr-image]][pr-url]
|
[![PRs Welcome][pr-image]][pr-url]
|
||||||
[![codecov][codecov-image]][codecov-url]
|
[![codecov][codecov-image]][codecov-url]
|
||||||
|
|
||||||
@ -107,8 +106,6 @@ Made with 💖
|
|||||||
[github-actions-workflow-url]: https://github.com/dolanmiu/docx/actions
|
[github-actions-workflow-url]: https://github.com/dolanmiu/docx/actions
|
||||||
[snky-image]: https://snyk.io/test/github/dolanmiu/docx/badge.svg
|
[snky-image]: https://snyk.io/test/github/dolanmiu/docx/badge.svg
|
||||||
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
|
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
|
||||||
[gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg
|
|
||||||
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
|
||||||
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||||
[pr-url]: http://makeapullrequest.com
|
[pr-url]: http://makeapullrequest.com
|
||||||
[codecov-image]: https://codecov.io/gh/dolanmiu/docx/branch/master/graph/badge.svg
|
[codecov-image]: https://codecov.io/gh/dolanmiu/docx/branch/master/graph/badge.svg
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<script src="../build/index.umd.cjs"></script>
|
<script src="../build/index.umd.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@ import inquirer from "inquirer";
|
|||||||
import { $ } from "execa";
|
import { $ } from "execa";
|
||||||
|
|
||||||
export type Answers = {
|
export type Answers = {
|
||||||
type: "list" | "number";
|
readonly type: "list" | "number";
|
||||||
demoNumber?: number;
|
readonly demoNumber?: number;
|
||||||
demoFile?: number;
|
readonly demoFile?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const dir = "./demo";
|
const dir = "./demo";
|
||||||
@ -15,8 +15,7 @@ const fileNames = fs.readdirSync(dir);
|
|||||||
|
|
||||||
const keys = fileNames.map((f) => path.parse(f).name);
|
const keys = fileNames.map((f) => path.parse(f).name);
|
||||||
const getFileNumber = (file: string): number => {
|
const getFileNumber = (file: string): number => {
|
||||||
const nameParts = file.split("-");
|
const [firstPart] = file.split("-");
|
||||||
const firstPart = nameParts[0];
|
|
||||||
|
|
||||||
return Number(firstPart);
|
return Number(firstPart);
|
||||||
};
|
};
|
||||||
@ -35,15 +34,15 @@ const answers = await inquirer.prompt<Answers>([
|
|||||||
name: "demoFile",
|
name: "demoFile",
|
||||||
message: "What demo do you wish to run?",
|
message: "What demo do you wish to run?",
|
||||||
choices: demoFiles,
|
choices: demoFiles,
|
||||||
filter: (input) => parseInt(input.split("-")[0]),
|
filter: (input) => parseInt(input.split("-")[0], 10),
|
||||||
when: (answers) => answers.type === "list",
|
when: (a) => a.type === "list",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "number",
|
type: "number",
|
||||||
name: "demoNumber",
|
name: "demoNumber",
|
||||||
message: "What demo do you wish to run? (Enter a number)",
|
message: "What demo do you wish to run? (Enter a number)",
|
||||||
default: 1,
|
default: 1,
|
||||||
when: (answers) => answers.type === "number",
|
when: (a) => a.type === "number",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -56,6 +55,7 @@ if (files.length === 0) {
|
|||||||
const filePath = path.join(dir, files[0]);
|
const filePath = path.join(dir, files[0]);
|
||||||
|
|
||||||
console.log(`Running demo ${demoNumber}: ${files[0]}`);
|
console.log(`Running demo ${demoNumber}: ${files[0]}`);
|
||||||
await $`ts-node --project demo/tsconfig.json ${filePath}`;
|
const { stdout } = await $`ts-node --project demo/tsconfig.json ${filePath}`;
|
||||||
|
console.log(stdout);
|
||||||
console.log("Successfully created document!");
|
console.log("Successfully created document!");
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# Bullets and Numbering
|
# Bullets and Numbering
|
||||||
|
|
||||||
|
!> Bullets and Numbering requires an understanding of [Sections](usage/sections.md) and [Paragraphs](usage/paragraph.md).
|
||||||
|
|
||||||
`docx` is quite flexible in its bullets and numbering system, allowing
|
`docx` is quite flexible in its bullets and numbering system, allowing
|
||||||
the user great freedom in how bullets and numbers are to be styled and
|
the user great freedom in how bullets and numbers are to be styled and
|
||||||
displayed. E.g., numbers can be shown using Arabic numerals, roman
|
displayed. E.g., numbers can be shown using Arabic numerals, roman
|
||||||
@ -8,112 +10,128 @@ format also supports re-using bullets/numbering styles throughout the
|
|||||||
document, so that different lists using the same style need not
|
document, so that different lists using the same style need not
|
||||||
redefine them.
|
redefine them.
|
||||||
|
|
||||||
Because of this flexibility, bullets and numbering in DOCX involves a
|
## Configuration
|
||||||
couple of moving pieces:
|
|
||||||
|
|
||||||
1. Document-level bullets/numbering definitions (abstract)
|
Numbering is configured by adding config into `Document`:
|
||||||
2. Document-level bullets/numbering definitions (concrete)
|
|
||||||
3. Paragraph-level bullets/numbering selection
|
|
||||||
|
|
||||||
## Document-level bullets/numbering definitions (abstract)
|
|
||||||
|
|
||||||
Every document contains a set of abstract bullets/numbering
|
|
||||||
definitions which define the formatting and layout of paragraphs using
|
|
||||||
those bullets/numbering. An abstract numbering system defines how
|
|
||||||
bullets/numbers are to be shown for lists, including any sublists that
|
|
||||||
may be used. Thus each abstract definition includes a series of
|
|
||||||
_levels_ which form a sequence starting at 0 indicating the top-level
|
|
||||||
list look and increasing from there to describe the sublists, then
|
|
||||||
sub-sublists, etc. Each level includes the following properties:
|
|
||||||
|
|
||||||
* **level**: This is its 0-based index in the definition stack
|
|
||||||
* **numberFormat**: This indicates how the bullet or number should be
|
|
||||||
generated. Options include `bullet` (meaning don't count), `decimal`
|
|
||||||
(arabic numerals), `upperRoman`, `lowerRoman`, `hex`, and many
|
|
||||||
more.
|
|
||||||
* **levelText**: This is a format string using the output of the
|
|
||||||
`numberFormat` function and generating a string to insert before
|
|
||||||
every item in the list. You may use `%1`, `%2`, ... to reference the
|
|
||||||
numbers from each numbering level before this one. Thus a level
|
|
||||||
text of `%d)` with a number format of `lowerLetter` would result in
|
|
||||||
the sequence "a)", "b)", ...
|
|
||||||
* and a few others, which you can see in the OOXML spec section 17.9.6
|
|
||||||
|
|
||||||
## Document-level bullets/numbering definitions (concrete)
|
|
||||||
|
|
||||||
Concrete definitions are sort of like concrete subclasses of the
|
|
||||||
abstract definitions. They indicate their parent and are allowed to
|
|
||||||
override certain level definitions. Thus two lists that differ only in
|
|
||||||
how sub-sub-lists are to be displayed can share the same abstract
|
|
||||||
numbering definition and have slightly different concrete definitions.
|
|
||||||
|
|
||||||
## Paragraph-level bullets/numbering selection
|
|
||||||
|
|
||||||
In order to use a bullets/numbering definition (which must be
|
|
||||||
concrete), paragraphs need to select it, similar to applying a CSS
|
|
||||||
class to an element, using both the concrete numbering definition ID
|
|
||||||
and the level number that the paragraph should be at. Additionally, MS
|
|
||||||
Word and LibreOffice typically apply a "ListParagraph" style to
|
|
||||||
paragraphs that are being numbered.
|
|
||||||
|
|
||||||
## Using bullets/numbering in `docx`
|
|
||||||
|
|
||||||
`docx` includes a pre-defined bullet style which you can add to your
|
|
||||||
paragraphs using `para.bullets()`. If you require different bullet
|
|
||||||
styles or numbering of any kind, you'll have to use the
|
|
||||||
`docx.Numbering` class.
|
|
||||||
|
|
||||||
First you need to create a new numbering container class and use it to
|
|
||||||
create your abstract numbering style, define your levels, and create
|
|
||||||
your concrete numbering style:
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const numbering = new docx.Numbering();
|
new Document({
|
||||||
|
numbering: {
|
||||||
const abstractNum = numbering.createAbstractNumbering();
|
config: [...]
|
||||||
abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new Indent(720, 260));
|
}
|
||||||
abstractNum.createLevel(1, "decimal", "%2.", "start").addParagraphProperty(new Indent(1440, 980));
|
})
|
||||||
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(new Indent(2160, 1700));
|
|
||||||
|
|
||||||
const concrete = numbering.createConcreteNumbering(abstractNum);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then apply your concrete style to paragraphs using the
|
Each `config` entry includes the following properties:
|
||||||
`setNumbering` method:
|
|
||||||
|
. Each level includes the following properties:
|
||||||
|
|
||||||
|
| Property | Type | Notes | Possible Values |
|
||||||
|
| --------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| reference | `string` | Required | A unique `string` |
|
||||||
|
| levels | `ILevelOptions[]` | Required | a series of _levels_ which form a sequence starting at 0 indicating the top-level list look and increasing from there to describe the sublists, then sub-sublists, etc |
|
||||||
|
|
||||||
|
### Level Options
|
||||||
|
|
||||||
|
Levels define the numbering definition itself, what it looks like, the indention, the alignment and the style. The reason why it is an array is because it allows the ability to create sub-lists. A sub list will have a different configuration because you may want the sub-list to have a different indentation or different bullet.
|
||||||
|
|
||||||
|
| Property | Type | Notes | Possible Values |
|
||||||
|
| --------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| level | `number` | Required | The list level this definition is for. `0` is for the root level, `1` is for a sub list, `2` is for a sub-sub-list etc. |
|
||||||
|
| format | `LevelFormat` | Optional | `DECIMAL`, `UPPER_ROMAN`, `LOWER_ROMAN`, `UPPER_LETTER`, `LOWER_LETTER`, `ORDINAL`, `CARDINAL_TEXT`, `ORDINAL_TEXT`, `HEX`, `CHICAGO`, `IDEOGRAPH__DIGITAL`, `JAPANESE_COUNTING`, `AIUEO`, `IROHA`, `DECIMAL_FULL_WIDTH`, `DECIMAL_HALF_WIDTH`, `JAPANESE_LEGAL`, `JAPANESE_DIGITAL_TEN_THOUSAND`, `DECIMAL_ENCLOSED_CIRCLE`, `DECIMAL_FULL_WIDTH2`, `AIUEO_FULL_WIDTH`, `IROHA_FULL_WIDTH`, `DECIMAL_ZERO`, `BULLET`, `GANADA`, `CHOSUNG`, `DECIMAL_ENCLOSED_FULLSTOP`, `DECIMAL_ENCLOSED_PARENTHESES`, `DECIMAL_ENCLOSED_CIRCLE_CHINESE`, `IDEOGRAPH_ENCLOSED_CIRCLE`, `IDEOGRAPH_TRADITIONAL`, `IDEOGRAPH_ZODIAC`, `IDEOGRAPH_ZODIAC_TRADITIONAL`, `TAIWANESE_COUNTING`, `IDEOGRAPH_LEGAL_TRADITIONAL`, `TAIWANESE_COUNTING_THOUSAND`, `TAIWANESE_DIGITAL`, `CHINESE_COUNTING`, `CHINESE_LEGAL_SIMPLIFIED`, `CHINESE_COUNTING_THOUSAND`, `KOREAN_DIGITAL`, `KOREAN_COUNTING`, `KOREAN_LEGAL`, `KOREAN_DIGITAL2`, `VIETNAMESE_COUNTING`, `RUSSIAN_LOWER`, `RUSSIAN_UPPER`, `NONE`, `NUMBER_IN_DASH`, `HEBREW1`, `HEBREW2`, `ARABIC_ALPHA`, `ARABIC_ABJAD`, `HINDI_VOWELS`, `HINDI_CONSONANTS`, `HINDI_NUMBERS`, `HINDI_COUNTING`, `THAI_LETTERS`, `THAI_NUMBERS`, `THAI_COUNTING`, `BAHT_TEXT`, `DOLLAR_TEXT`, `CUSTOM` |
|
||||||
|
| text | `string` | Optional | A unique `string` to describe the shape of the bullet |
|
||||||
|
| alignment | `string` | Required | `START`, `CENTER`, `END`, `BOTH`, `MEDIUM_KASHIDA`, `DISTRIBUTE`, `NUM_TAB`, `HIGH_KASHIDA`, `LOW_KASHIDA`, `THAI_DISTRIBUTE`, `LEFT`, `RIGHT`, `JUSTIFIED` |
|
||||||
|
| style | `string` | Optional | [Sections](usage/styling-with-js.md) |
|
||||||
|
|
||||||
|
## Using ordered lists in `docx`
|
||||||
|
|
||||||
|
Add a `numbering` section to the `Document` to numbering style, define your levels. Use `LevelFormat.UPPER_ROMAN` for the `format` in `levels`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
topLevelP.setNumbering(concrete, 0);
|
const doc = new Document({
|
||||||
subP.setNumbering(concrete, 1);
|
...
|
||||||
subSubP.setNumbering(concrete, 2);
|
numbering: {
|
||||||
```
|
config: [
|
||||||
|
{
|
||||||
## Unindent numbering
|
reference: "my-numbering",
|
||||||
|
levels: [
|
||||||
Default:1. test
|
{
|
||||||
|
|
||||||
After:1.test
|
|
||||||
|
|
||||||
Use default numbering have indent,If you want unindent numbering
|
|
||||||
|
|
||||||
How to custom number see the demo:
|
|
||||||
https://runkit.com/dolanmiu/docx-demo3
|
|
||||||
|
|
||||||
```ts
|
|
||||||
|
|
||||||
enum LevelSuffix {
|
|
||||||
NOTHING = "nothing",
|
|
||||||
SPACE = "space",
|
|
||||||
TAB = "tab"
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom numbering
|
|
||||||
const levels=[
|
|
||||||
{
|
|
||||||
level: 0,
|
level: 0,
|
||||||
format: "decimal",
|
format: LevelFormat.UPPER_ROMAN,
|
||||||
text: "%1.",
|
text: "%1",
|
||||||
alignment: AlignmentType.START,
|
alignment: AlignmentType.START,
|
||||||
suffix: LevelSuffix.NOTHING, // Cancel intent
|
style: {
|
||||||
}]
|
paragraph: {
|
||||||
|
indent: { left: 2880, hanging: 2420 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
And then on a `Paragraph`, we can add use the numbering created:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
new Paragraph({
|
||||||
|
text: "Hey you!",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-numbering",
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
```
|
||||||
|
|
||||||
|
## Un-ordered lists / Bullet points
|
||||||
|
|
||||||
|
Add a `numbering` section to the `Document` to numbering style, define your levels. Use `LevelFormat.BULLET` for the `format` in `levels`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const doc = new Document({
|
||||||
|
...
|
||||||
|
numbering: {
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
reference: "my-bullet-points",
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
level: 0,
|
||||||
|
format: LevelFormat.BULLET,
|
||||||
|
text: "\u1F60",
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
style: {
|
||||||
|
paragraph: {
|
||||||
|
indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.25) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
And then on a `Paragraph`, we can add use the numbering created:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
new Paragraph({
|
||||||
|
text: "Hey you!",
|
||||||
|
numbering: {
|
||||||
|
reference: "my-bullet-points",
|
||||||
|
level: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/3-numbering-and-bullet-points.ts ":include")
|
||||||
|
|
||||||
|
_Source: https://github.com/dolanmiu/docx/blob/master/demo/3-numbering-and-bullet-points.ts_
|
||||||
|
@ -9,11 +9,16 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
*Note*: Font and color selection from the theme are currently not supported.
|
||||||
|
|
||||||
3. You can even create a totally new `Style`:
|
3. You can even create a totally new `Style`:
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
*Note*: When selecting the style type, it is important to consider the component being used.
|
||||||
|
|
||||||
|
|
||||||
4. Save
|
4. Save
|
||||||
5. Re-name the saved `.docx` file to `.zip` and un-zip
|
5. Re-name the saved `.docx` file to `.zip` and un-zip
|
||||||
6. Find `styles.xml`
|
6. Find `styles.xml`
|
||||||
|
4881
package-lock.json
generated
4881
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@ -1,20 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "8.2.0",
|
"version": "8.2.4",
|
||||||
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "build/index.umd.cjs",
|
"main": "build/index.umd.js",
|
||||||
"module": "./build/index.js",
|
"module": "./build/index.mjs",
|
||||||
"types": "./build/index.d.ts",
|
"types": "./build/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"browser": {
|
|
||||||
"default": "./build/index.umd.cjs"
|
|
||||||
},
|
|
||||||
"require": "./build/index.cjs",
|
"require": "./build/index.cjs",
|
||||||
"types": "./build/index.d.ts",
|
"types": "./build/index.d.ts",
|
||||||
"import": "./build/index.js",
|
"import": "./build/index.mjs",
|
||||||
"default": "./build/index.js"
|
"default": "./build/index.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -58,7 +55,6 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"fflate": "^0.8.0",
|
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^4.0.2",
|
||||||
"xml": "^1.0.1",
|
"xml": "^1.0.1",
|
||||||
@ -71,16 +67,15 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://docx.js.org",
|
"homepage": "https://docx.js.org",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@esbuild/win32-x64": "^0.18.3",
|
|
||||||
"@types/inquirer": "^9.0.3",
|
"@types/inquirer": "^9.0.3",
|
||||||
"@types/prompt": "^1.1.1",
|
"@types/prompt": "^1.1.1",
|
||||||
"@types/unzipper": "^0.10.4",
|
"@types/unzipper": "^0.10.4",
|
||||||
"@types/xml": "^1.0.8",
|
"@types/xml": "^1.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
||||||
"@typescript-eslint/parser": "^5.36.1",
|
"@typescript-eslint/parser": "^6.9.1",
|
||||||
"@vitest/coverage-v8": "^0.32.0",
|
"@vitest/coverage-v8": "^0.33.0",
|
||||||
"@vitest/ui": "^0.32.0",
|
"@vitest/ui": "^0.33.0",
|
||||||
"cspell": "^6.2.2",
|
"cspell": "^7.3.8",
|
||||||
"docsify-cli": "^4.3.0",
|
"docsify-cli": "^4.3.0",
|
||||||
"eslint": "^8.23.0",
|
"eslint": "^8.23.0",
|
||||||
"eslint-plugin-functional": "^5.0.8",
|
"eslint-plugin-functional": "^5.0.8",
|
||||||
@ -88,23 +83,23 @@
|
|||||||
"eslint-plugin-jsdoc": "^46.2.6",
|
"eslint-plugin-jsdoc": "^46.2.6",
|
||||||
"eslint-plugin-no-null": "^1.0.2",
|
"eslint-plugin-no-null": "^1.0.2",
|
||||||
"eslint-plugin-prefer-arrow": "^1.2.3",
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||||
"eslint-plugin-unicorn": "^47.0.0",
|
"eslint-plugin-unicorn": "^48.0.1",
|
||||||
"execa": "^7.1.1",
|
"execa": "^8.0.1",
|
||||||
"glob": "^10.2.7",
|
"glob": "^10.2.7",
|
||||||
"inquirer": "^9.2.7",
|
"inquirer": "^9.2.7",
|
||||||
"jsdom": "^22.1.0",
|
"jsdom": "^22.1.0",
|
||||||
"pre-commit": "^1.2.2",
|
"pre-commit": "^1.2.2",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^3.0.0",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"tsconfig-paths": "^4.0.0",
|
"tsconfig-paths": "^4.0.0",
|
||||||
"typedoc": "^0.24.8",
|
"typedoc": "^0.24.8",
|
||||||
"typescript": "5.1.3",
|
"typescript": "5.1.6",
|
||||||
"unzipper": "^0.10.11",
|
"unzipper": "^0.10.11",
|
||||||
"vite": "^4.3.2",
|
"vite": "^4.3.2",
|
||||||
"vite-plugin-dts": "^2.3.0",
|
"vite-plugin-dts": "^3.3.1",
|
||||||
"vite-plugin-node-polyfills": "^0.9.0",
|
"vite-plugin-node-polyfills": "^0.9.0",
|
||||||
"vite-tsconfig-paths": "^4.2.0",
|
"vite-tsconfig-paths": "^4.2.0",
|
||||||
"vitest": "^0.32.0"
|
"vitest": "^0.33.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
|
@ -5,7 +5,6 @@ import { FooterWrapper } from "@file/footer-wrapper";
|
|||||||
import { HeaderWrapper } from "@file/header-wrapper";
|
import { HeaderWrapper } from "@file/header-wrapper";
|
||||||
import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align";
|
import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align";
|
||||||
import { OnOffElement, XmlComponent } from "@file/xml-components";
|
import { OnOffElement, XmlComponent } from "@file/xml-components";
|
||||||
import { PositiveUniversalMeasure, UniversalMeasure } from "@util/values";
|
|
||||||
|
|
||||||
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
|
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
|
||||||
import { Columns, IColumnsAttributes } from "./properties/columns";
|
import { Columns, IColumnsAttributes } from "./properties/columns";
|
||||||
@ -76,10 +75,10 @@ export interface ISectionPropertiesOptions {
|
|||||||
// </xsd:group>
|
// </xsd:group>
|
||||||
|
|
||||||
export const sectionMarginDefaults = {
|
export const sectionMarginDefaults = {
|
||||||
TOP: "1in" as UniversalMeasure,
|
TOP: 1440,
|
||||||
RIGHT: "1in" as PositiveUniversalMeasure,
|
RIGHT: 1440,
|
||||||
BOTTOM: "1in" as UniversalMeasure,
|
BOTTOM: 1440,
|
||||||
LEFT: "1in" as PositiveUniversalMeasure,
|
LEFT: 1440,
|
||||||
HEADER: 708,
|
HEADER: 708,
|
||||||
FOOTER: 708,
|
FOOTER: 708,
|
||||||
GUTTER: 0,
|
GUTTER: 0,
|
||||||
|
@ -80,6 +80,7 @@ export class Document extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public add(item: Paragraph | Table | TableOfContents | ConcreteHyperlink): Document {
|
public add(item: Paragraph | Table | TableOfContents | ConcreteHyperlink): Document {
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
this.body.push(item);
|
this.body.push(item);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,11 @@ export class FooterWrapper implements IViewWrapper {
|
|||||||
private readonly footer: Footer;
|
private readonly footer: Footer;
|
||||||
private readonly relationships: Relationships;
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
public constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) {
|
public constructor(
|
||||||
|
private readonly media: Media,
|
||||||
|
referenceId: number,
|
||||||
|
initContent?: XmlComponent,
|
||||||
|
) {
|
||||||
this.footer = new Footer(referenceId, initContent);
|
this.footer = new Footer(referenceId, initContent);
|
||||||
this.relationships = new Relationships();
|
this.relationships = new Relationships();
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,11 @@ export class HeaderWrapper implements IViewWrapper {
|
|||||||
private readonly header: Header;
|
private readonly header: Header;
|
||||||
private readonly relationships: Relationships;
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
public constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) {
|
public constructor(
|
||||||
|
private readonly media: Media,
|
||||||
|
referenceId: number,
|
||||||
|
initContent?: XmlComponent,
|
||||||
|
) {
|
||||||
this.header = new Header(referenceId, initContent);
|
this.header = new Header(referenceId, initContent);
|
||||||
this.relationships = new Relationships();
|
this.relationships = new Relationships();
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ export class Numbering extends XmlComponent {
|
|||||||
abstractNumId: abstractNumbering.id,
|
abstractNumId: abstractNumbering.id,
|
||||||
reference,
|
reference,
|
||||||
instance,
|
instance,
|
||||||
overrideLevel:
|
overrideLevels: [
|
||||||
firstLevelStartNumber && Number.isInteger(firstLevelStartNumber)
|
firstLevelStartNumber && Number.isInteger(firstLevelStartNumber)
|
||||||
? {
|
? {
|
||||||
num: 0,
|
num: 0,
|
||||||
@ -226,6 +226,7 @@ export class Numbering extends XmlComponent {
|
|||||||
num: 0,
|
num: 0,
|
||||||
start: 1,
|
start: 1,
|
||||||
},
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
this.concreteNumberingMap.set(fullReference, new ConcreteNumbering(concreteNumberingSettings));
|
this.concreteNumberingMap.set(fullReference, new ConcreteNumbering(concreteNumberingSettings));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// http://officeopenxml.com/WPparagraphProperties.php
|
// http://officeopenxml.com/WPparagraphProperties.php
|
||||||
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_suppressLineNumbers_topic_ID0ECJAO.html
|
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_suppressLineNumbers_topic_ID0ECJAO.html
|
||||||
|
/* eslint-disable functional/immutable-data */
|
||||||
import { IContext, IgnoreIfEmptyXmlComponent, IXmlableObject, OnOffElement, XmlComponent } from "@file/xml-components";
|
import { IContext, IgnoreIfEmptyXmlComponent, IXmlableObject, OnOffElement, XmlComponent } from "@file/xml-components";
|
||||||
import { DocumentWrapper } from "../document-wrapper";
|
import { DocumentWrapper } from "../document-wrapper";
|
||||||
import { IShadingAttributesProperties, Shading } from "../shading";
|
import { IShadingAttributesProperties, Shading } from "../shading";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// https://www.ecma-international.org/wp-content/uploads/ECMA-376-1_5th_edition_december_2016.zip page 297, section 17.3.2.21
|
// https://www.ecma-international.org/wp-content/uploads/ECMA-376-1_5th_edition_december_2016.zip page 297, section 17.3.2.21
|
||||||
|
/* eslint-disable functional/immutable-data */
|
||||||
import { BorderElement, IBorderOptions } from "@file/border";
|
import { BorderElement, IBorderOptions } from "@file/border";
|
||||||
import { IShadingAttributesProperties, Shading } from "@file/shading";
|
import { IShadingAttributesProperties, Shading } from "@file/shading";
|
||||||
import { ChangeAttributes, IChangedAttributesProperties } from "@file/track-revision/track-revision";
|
import { ChangeAttributes, IChangedAttributesProperties } from "@file/track-revision/track-revision";
|
||||||
|
@ -55,6 +55,7 @@ describe("ImportedXmlComponent", () => {
|
|||||||
otherAttr: "2",
|
otherAttr: "2",
|
||||||
};
|
};
|
||||||
importedXmlComponent = new ImportedXmlComponent("w:test", attributes);
|
importedXmlComponent = new ImportedXmlComponent("w:test", attributes);
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
importedXmlComponent.push(new ImportedXmlComponent("w:child"));
|
importedXmlComponent.push(new ImportedXmlComponent("w:child"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ export const convertToXmlComponent = (element: XmlElement): ImportedXmlComponent
|
|||||||
for (const childElm of childElements) {
|
for (const childElm of childElements) {
|
||||||
const child = convertToXmlComponent(childElm);
|
const child = convertToXmlComponent(childElm);
|
||||||
if (child !== undefined) {
|
if (child !== undefined) {
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
xmlComponent.push(child);
|
xmlComponent.push(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,6 +61,7 @@ export class ImportedXmlComponent extends XmlComponent {
|
|||||||
public constructor(rootKey: string, _attr?: any) {
|
public constructor(rootKey: string, _attr?: any) {
|
||||||
super(rootKey);
|
super(rootKey);
|
||||||
if (_attr) {
|
if (_attr) {
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
this.root.push(new ImportedXmlComponentAttributes(_attr));
|
this.root.push(new ImportedXmlComponentAttributes(_attr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,13 @@ describe("XmlComponent", () => {
|
|||||||
});
|
});
|
||||||
it("should handle children elements", () => {
|
it("should handle children elements", () => {
|
||||||
const xmlComponent = new TestComponent("w:test");
|
const xmlComponent = new TestComponent("w:test");
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
xmlComponent.push(
|
xmlComponent.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
val: "test",
|
val: "test",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
xmlComponent.push(new TestComponent("innerTest"));
|
xmlComponent.push(new TestComponent("innerTest"));
|
||||||
|
|
||||||
const tree = new Formatter().format(xmlComponent);
|
const tree = new Formatter().format(xmlComponent);
|
||||||
@ -43,6 +45,7 @@ describe("XmlComponent", () => {
|
|||||||
});
|
});
|
||||||
it("should hoist attrs if only attrs are present", () => {
|
it("should hoist attrs if only attrs are present", () => {
|
||||||
const xmlComponent = new TestComponent("w:test");
|
const xmlComponent = new TestComponent("w:test");
|
||||||
|
// eslint-disable-next-line functional/immutable-data
|
||||||
xmlComponent.push(
|
xmlComponent.push(
|
||||||
new Attributes({
|
new Attributes({
|
||||||
val: "test",
|
val: "test",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export interface IXmlAttribute {
|
export interface IXmlAttribute {
|
||||||
readonly [key: string]: string | number | boolean;
|
readonly [key: string]: string | number | boolean;
|
||||||
}
|
}
|
||||||
export interface IXmlableObject extends Object {
|
export interface IXmlableObject extends Record<string, unknown> {
|
||||||
// readonly _attr?: IXmlAttribute;
|
// readonly _attr?: IXmlAttribute;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
readonly [key: string]: any;
|
readonly [key: string]: any;
|
||||||
|
@ -49,11 +49,12 @@ export type IPatch = ParagraphPatch | FilePatch;
|
|||||||
|
|
||||||
export interface PatchDocumentOptions {
|
export interface PatchDocumentOptions {
|
||||||
readonly patches: { readonly [key: string]: IPatch };
|
readonly patches: { readonly [key: string]: IPatch };
|
||||||
|
readonly keepOriginalStyles?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageReplacer = new ImageReplacer();
|
const imageReplacer = new ImageReplacer();
|
||||||
|
|
||||||
export const patchDocument = async (data: InputDataType, options: PatchDocumentOptions): Promise<Buffer> => {
|
export const patchDocument = async (data: InputDataType, options: PatchDocumentOptions): Promise<Uint8Array> => {
|
||||||
const zipContent = await JSZip.loadAsync(data);
|
const zipContent = await JSZip.loadAsync(data);
|
||||||
const contexts = new Map<string, IContext>();
|
const contexts = new Map<string, IContext>();
|
||||||
const file = {
|
const file = {
|
||||||
@ -68,11 +69,11 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
|
|||||||
const hyperlinkRelationshipAdditions: IHyperlinkRelationshipAddition[] = [];
|
const hyperlinkRelationshipAdditions: IHyperlinkRelationshipAddition[] = [];
|
||||||
let hasMedia = false;
|
let hasMedia = false;
|
||||||
|
|
||||||
const binaryContentMap = new Map<string, Buffer>();
|
const binaryContentMap = new Map<string, Uint8Array>();
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(zipContent.files)) {
|
for (const [key, value] of Object.entries(zipContent.files)) {
|
||||||
if (!key.endsWith(".xml") && !key.endsWith(".rels")) {
|
if (!key.endsWith(".xml") && !key.endsWith(".rels")) {
|
||||||
binaryContentMap.set(key, await value.async("nodebuffer"));
|
binaryContentMap.set(key, await value.async("uint8array"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +129,7 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
|
|||||||
patchText,
|
patchText,
|
||||||
renderedParagraphs,
|
renderedParagraphs,
|
||||||
context,
|
context,
|
||||||
|
options.keepOriginalStyles,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +215,7 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
|
|||||||
}
|
}
|
||||||
|
|
||||||
return zip.generateAsync({
|
return zip.generateAsync({
|
||||||
type: "nodebuffer",
|
type: "uint8array",
|
||||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
compression: "DEFLATE",
|
compression: "DEFLATE",
|
||||||
});
|
});
|
||||||
|
@ -43,6 +43,7 @@ export const replaceTokenInParagraphElement = ({
|
|||||||
patchTextElement(paragraphElement.elements![run.index].elements![index], firstPart);
|
patchTextElement(paragraphElement.elements![run.index].elements![index], firstPart);
|
||||||
replaceMode = ReplaceMode.MIDDLE;
|
replaceMode = ReplaceMode.MIDDLE;
|
||||||
continue;
|
continue;
|
||||||
|
/* c8 ignore next 2 */
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ReplaceMode.MIDDLE:
|
case ReplaceMode.MIDDLE:
|
||||||
@ -59,6 +60,7 @@ export const replaceTokenInParagraphElement = ({
|
|||||||
patchTextElement(paragraphElement.elements![run.index].elements![index], "");
|
patchTextElement(paragraphElement.elements![run.index].elements![index], "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
/* c8 ignore next */
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,28 @@ const MOCK_JSON = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:p",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:rPr",
|
||||||
|
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:t",
|
||||||
|
elements: [{ type: "text", text: "What a {{bold}} text!" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -115,6 +137,93 @@ describe("replacer", () => {
|
|||||||
expect(JSON.stringify(output)).to.contain("Delightful Header");
|
expect(JSON.stringify(output)).to.contain("Delightful Header");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should replace paragraph type keeping original styling if keepOriginalStyles is true", () => {
|
||||||
|
const output = replacer(
|
||||||
|
MOCK_JSON,
|
||||||
|
{
|
||||||
|
type: PatchType.PARAGRAPH,
|
||||||
|
children: [new TextRun("sweet")],
|
||||||
|
},
|
||||||
|
"{{bold}}",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: "What a {{bold}} text!",
|
||||||
|
runs: [
|
||||||
|
{
|
||||||
|
text: "What a {{bold}} text!",
|
||||||
|
parts: [{ text: "What a {{bold}} text!", index: 1, start: 0, end: 21 }],
|
||||||
|
index: 0,
|
||||||
|
start: 0,
|
||||||
|
end: 21,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
path: [0, 0, 1],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
file: {} as unknown as File,
|
||||||
|
viewWrapper: {
|
||||||
|
Relationships: {},
|
||||||
|
} as unknown as IViewWrapper,
|
||||||
|
stack: [],
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(JSON.stringify(output)).to.contain("sweet");
|
||||||
|
expect(output.elements![0].elements![1].elements).toMatchObject([
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:rPr",
|
||||||
|
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:t",
|
||||||
|
elements: [{ type: "text", text: "What a " }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:rPr",
|
||||||
|
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:t",
|
||||||
|
elements: [{ type: "text", text: "sweet" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:rPr",
|
||||||
|
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:t",
|
||||||
|
elements: [{ type: "text", text: " text!" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it("should replace document type", () => {
|
it("should replace document type", () => {
|
||||||
const output = replacer(
|
const output = replacer(
|
||||||
MOCK_JSON,
|
MOCK_JSON,
|
||||||
|
@ -20,6 +20,7 @@ export const replacer = (
|
|||||||
patchText: string,
|
patchText: string,
|
||||||
renderedParagraphs: readonly IRenderedParagraphNode[],
|
renderedParagraphs: readonly IRenderedParagraphNode[],
|
||||||
context: IContext,
|
context: IContext,
|
||||||
|
keepOriginalStyles: boolean = false,
|
||||||
): Element => {
|
): Element => {
|
||||||
for (const renderedParagraph of renderedParagraphs) {
|
for (const renderedParagraph of renderedParagraphs) {
|
||||||
const textJson = patch.children
|
const textJson = patch.children
|
||||||
@ -47,9 +48,30 @@ export const replacer = (
|
|||||||
|
|
||||||
const index = findRunElementIndexWithToken(paragraphElement, SPLIT_TOKEN);
|
const index = findRunElementIndexWithToken(paragraphElement, SPLIT_TOKEN);
|
||||||
|
|
||||||
const { left, right } = splitRunElement(paragraphElement.elements![index], SPLIT_TOKEN);
|
const runElementToBeReplaced = paragraphElement.elements![index];
|
||||||
|
const { left, right } = splitRunElement(runElementToBeReplaced, SPLIT_TOKEN);
|
||||||
|
|
||||||
|
let newRunElements = textJson;
|
||||||
|
let patchedRightElement = right;
|
||||||
|
|
||||||
|
if (keepOriginalStyles) {
|
||||||
|
const runElementNonTextualElements = runElementToBeReplaced.elements!.filter(
|
||||||
|
(e) => e.type === "element" && e.name !== "w:t",
|
||||||
|
);
|
||||||
|
|
||||||
|
newRunElements = textJson.map((e) => ({
|
||||||
|
...e,
|
||||||
|
elements: [...runElementNonTextualElements, ...e.elements!],
|
||||||
|
}));
|
||||||
|
|
||||||
|
patchedRightElement = {
|
||||||
|
...right,
|
||||||
|
elements: [...runElementNonTextualElements, ...right.elements!],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line functional/immutable-data
|
// eslint-disable-next-line functional/immutable-data
|
||||||
paragraphElement.elements!.splice(index, 1, left, ...textJson, right);
|
paragraphElement.elements!.splice(index, 1, left, ...newRunElements, patchedRightElement);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,10 @@ export default defineConfig({
|
|||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
dts(),
|
dts(),
|
||||||
nodePolyfills({
|
nodePolyfills({
|
||||||
exclude: ["fs"],
|
exclude: [],
|
||||||
|
globals: {
|
||||||
|
Buffer: false,
|
||||||
|
},
|
||||||
protocolImports: true,
|
protocolImports: true,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -26,7 +29,25 @@ export default defineConfig({
|
|||||||
lib: {
|
lib: {
|
||||||
entry: [resolve(__dirname, "src/index.ts")],
|
entry: [resolve(__dirname, "src/index.ts")],
|
||||||
name: "docx",
|
name: "docx",
|
||||||
fileName: "index",
|
fileName: (d) => {
|
||||||
|
if (d === "umd") {
|
||||||
|
return "index.umd.js";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d === "cjs") {
|
||||||
|
return "index.cjs";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d === "es") {
|
||||||
|
return "index.mjs";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d === "iife") {
|
||||||
|
return "index.iife.js";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
},
|
||||||
formats: ["iife", "es", "cjs", "umd"],
|
formats: ["iife", "es", "cjs", "umd"],
|
||||||
},
|
},
|
||||||
outDir: resolve(__dirname, "build"),
|
outDir: resolve(__dirname, "build"),
|
||||||
|
Reference in New Issue
Block a user