Compare commits

...

4 Commits
8.0.3 ... 8.0.4

Author SHA1 Message Date
b59fa6bb16 Version bump 2023-05-04 19:35:01 +01:00
c0c62001fe Merge pull request #2103 from VarLog/fix/convenience-functions-counters
fix: do not use static numeric counters
2023-05-04 18:33:51 +01:00
e77a7dfdcd tests: fixup tests and linting issues 2023-05-04 12:30:31 +03:00
a0437381e7 fix: do not use static numeric counters 2023-04-28 13:17:02 +03:00
11 changed files with 39 additions and 61 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "docx", "name": "docx",
"version": "8.0.3", "version": "8.0.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "docx", "name": "docx",
"version": "8.0.3", "version": "8.0.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/node": "^18.0.0", "@types/node": "^18.0.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "docx", "name": "docx",
"version": "8.0.3", "version": "8.0.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.",
"main": "build/index.js", "main": "build/index.js",
"scripts": { "scripts": {

View File

@ -1,8 +1,6 @@
import { assert, expect } from "chai"; import { assert, expect } from "chai";
import { SinonStub, stub } from "sinon";
import { Formatter } from "@export/formatter"; import { Formatter } from "@export/formatter";
import * as convenienceFunctions from "@util/convenience-functions";
import { Utility } from "tests/utility"; import { Utility } from "tests/utility";
@ -40,14 +38,6 @@ const createAnchor = (drawingOptions: IDrawingOptions): Anchor =>
); );
describe("Anchor", () => { describe("Anchor", () => {
before(() => {
stub(convenienceFunctions, "docPropertiesUniqueNumericId").callsFake(() => 0);
});
after(() => {
(convenienceFunctions.docPropertiesUniqueNumericId as SinonStub).restore();
});
let anchor: Anchor; let anchor: Anchor;
describe("#constructor()", () => { describe("#constructor()", () => {
@ -466,7 +456,7 @@ describe("Anchor", () => {
"wp:docPr": { "wp:docPr": {
_attr: { _attr: {
descr: "test", descr: "test",
id: 0, id: 1,
name: "test", name: "test",
title: "test", title: "test",
}, },

View File

@ -2,7 +2,7 @@
import { IContext, IXmlableObject, NextAttributeComponent, XmlComponent } from "@file/xml-components"; import { IContext, IXmlableObject, NextAttributeComponent, XmlComponent } from "@file/xml-components";
import { ConcreteHyperlink } from "@file/paragraph"; import { ConcreteHyperlink } from "@file/paragraph";
import { docPropertiesUniqueNumericId } from "@util/convenience-functions"; import { docPropertiesUniqueNumericIdGen } from "@util/convenience-functions";
import { createHyperlinkClick } from "./doc-properties-children"; import { createHyperlinkClick } from "./doc-properties-children";
@ -25,6 +25,8 @@ export interface DocPropertiesOptions {
} }
export class DocProperties extends XmlComponent { export class DocProperties extends XmlComponent {
private readonly docPropertiesUniqueNumericId = docPropertiesUniqueNumericIdGen();
public constructor({ name, description, title }: DocPropertiesOptions = { name: "", description: "", title: "" }) { public constructor({ name, description, title }: DocPropertiesOptions = { name: "", description: "", title: "" }) {
super("wp:docPr"); super("wp:docPr");
@ -32,7 +34,7 @@ export class DocProperties extends XmlComponent {
new NextAttributeComponent({ new NextAttributeComponent({
id: { id: {
key: "id", key: "id",
value: docPropertiesUniqueNumericId(), value: this.docPropertiesUniqueNumericId(),
}, },
name: { name: {
key: "name", key: "name",

View File

@ -1,9 +1,7 @@
import { expect } from "chai"; import { expect } from "chai";
import { SinonStub, stub } from "sinon";
import { IContext } from "@file/xml-components"; import { IContext } from "@file/xml-components";
import { Formatter } from "@export/formatter"; import { Formatter } from "@export/formatter";
import * as convenienceFunctions from "@util/convenience-functions";
import { ConcreteHyperlink, TextRun } from "../"; import { ConcreteHyperlink, TextRun } from "../";
import { Drawing, IDrawingOptions } from "./drawing"; import { Drawing, IDrawingOptions } from "./drawing";
@ -30,14 +28,6 @@ const createDrawing = (drawingOptions?: IDrawingOptions): Drawing =>
); );
describe("Drawing", () => { describe("Drawing", () => {
before(() => {
stub(convenienceFunctions, "docPropertiesUniqueNumericId").callsFake(() => 0);
});
after(() => {
(convenienceFunctions.docPropertiesUniqueNumericId as SinonStub).restore();
});
let currentBreak: Drawing; let currentBreak: Drawing;
describe("#constructor()", () => { describe("#constructor()", () => {
@ -78,7 +68,7 @@ describe("Drawing", () => {
"wp:docPr": { "wp:docPr": {
_attr: { _attr: {
descr: "", descr: "",
id: 0, id: 1,
name: "", name: "",
title: "", title: "",
}, },
@ -309,7 +299,7 @@ describe("Drawing", () => {
"wp:docPr": { "wp:docPr": {
_attr: { _attr: {
descr: "", descr: "",
id: 0, id: 1,
name: "", name: "",
title: "", title: "",
}, },
@ -543,7 +533,7 @@ describe("Drawing", () => {
{ {
_attr: { _attr: {
descr: "", descr: "",
id: 0, id: 1,
name: "", name: "",
title: "", title: "",
}, },

View File

@ -1,22 +1,10 @@
import { expect } from "chai"; import { expect } from "chai";
import { SinonStub, stub } from "sinon";
import { Formatter } from "@export/formatter"; import { Formatter } from "@export/formatter";
import * as convenienceFunctions from "@util/convenience-functions";
import { Numbering } from "./numbering"; import { Numbering } from "./numbering";
describe("Numbering", () => { describe("Numbering", () => {
before(() => {
stub(convenienceFunctions, "abstractNumUniqueNumericId").callsFake(() => 0);
stub(convenienceFunctions, "concreteNumUniqueNumericId").callsFake(() => 0);
});
after(() => {
(convenienceFunctions.abstractNumUniqueNumericId as SinonStub).restore();
(convenienceFunctions.concreteNumUniqueNumericId as SinonStub).restore();
});
describe("#constructor", () => { describe("#constructor", () => {
it("creates a default numbering with one abstract and one concrete instance", () => { it("creates a default numbering with one abstract and one concrete instance", () => {
const numbering = new Numbering({ const numbering = new Numbering({
@ -28,7 +16,7 @@ describe("Numbering", () => {
const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]); const abstractNums = tree["w:numbering"].filter((el) => el["w:abstractNum"]);
expect(abstractNums).to.have.lengthOf(1); expect(abstractNums).to.have.lengthOf(1);
expect(abstractNums[0]["w:abstractNum"]).to.deep.include.members([ expect(abstractNums[0]["w:abstractNum"]).to.deep.include.members([
{ _attr: { "w:abstractNumId": 0, "w15:restartNumberingAfterBreak": 0 } }, { _attr: { "w:abstractNumId": 1, "w15:restartNumberingAfterBreak": 0 } },
{ "w:multiLevelType": { _attr: { "w:val": "hybridMultilevel" } } }, { "w:multiLevelType": { _attr: { "w:val": "hybridMultilevel" } } },
]); ]);

View File

@ -2,7 +2,7 @@
// https://stackoverflow.com/questions/58622437/purpose-of-abstractnum-and-numberinginstance // https://stackoverflow.com/questions/58622437/purpose-of-abstractnum-and-numberinginstance
import { AlignmentType } from "@file/paragraph"; import { AlignmentType } from "@file/paragraph";
import { IContext, IXmlableObject, XmlComponent } from "@file/xml-components"; import { IContext, IXmlableObject, XmlComponent } from "@file/xml-components";
import { abstractNumUniqueNumericId, concreteNumUniqueNumericId, convertInchesToTwip } from "@util/convenience-functions"; import { abstractNumUniqueNumericIdGen, concreteNumUniqueNumericIdGen, convertInchesToTwip } from "@util/convenience-functions";
import { DocumentAttributes } from "../document/document-attributes"; import { DocumentAttributes } from "../document/document-attributes";
import { AbstractNumbering } from "./abstract-numbering"; import { AbstractNumbering } from "./abstract-numbering";
@ -30,6 +30,8 @@ export class Numbering extends XmlComponent {
private readonly abstractNumberingMap = new Map<string, AbstractNumbering>(); private readonly abstractNumberingMap = new Map<string, AbstractNumbering>();
private readonly concreteNumberingMap = new Map<string, ConcreteNumbering>(); private readonly concreteNumberingMap = new Map<string, ConcreteNumbering>();
private readonly referenceConfigMap = new Map<string, object>(); private readonly referenceConfigMap = new Map<string, object>();
private readonly abstractNumUniqueNumericId = abstractNumUniqueNumericIdGen();
private readonly concreteNumUniqueNumericId = concreteNumUniqueNumericIdGen();
public constructor(options: INumberingOptions) { public constructor(options: INumberingOptions) {
super("w:numbering"); super("w:numbering");
@ -55,7 +57,7 @@ export class Numbering extends XmlComponent {
}), }),
); );
const abstractNumbering = new AbstractNumbering(abstractNumUniqueNumericId(), [ const abstractNumbering = new AbstractNumbering(this.abstractNumUniqueNumericId(), [
{ {
level: 0, level: 0,
format: LevelFormat.BULLET, format: LevelFormat.BULLET,
@ -176,7 +178,7 @@ export class Numbering extends XmlComponent {
this.abstractNumberingMap.set("default-bullet-numbering", abstractNumbering); this.abstractNumberingMap.set("default-bullet-numbering", abstractNumbering);
for (const con of options.config) { for (const con of options.config) {
this.abstractNumberingMap.set(con.reference, new AbstractNumbering(abstractNumUniqueNumericId(), con.levels)); this.abstractNumberingMap.set(con.reference, new AbstractNumbering(this.abstractNumUniqueNumericId(), con.levels));
this.referenceConfigMap.set(con.reference, con.levels); this.referenceConfigMap.set(con.reference, con.levels);
} }
} }
@ -209,7 +211,7 @@ export class Numbering extends XmlComponent {
const firstLevelStartNumber = referenceConfigLevels && referenceConfigLevels[0].start; const firstLevelStartNumber = referenceConfigLevels && referenceConfigLevels[0].start;
const concreteNumberingSettings = { const concreteNumberingSettings = {
numId: concreteNumUniqueNumericId(), numId: this.concreteNumUniqueNumericId(),
abstractNumId: abstractNumbering.id, abstractNumId: abstractNumbering.id,
reference, reference,
instance, instance,

View File

@ -1,17 +1,19 @@
// http://officeopenxml.com/WPbookmark.php // http://officeopenxml.com/WPbookmark.php
import { XmlComponent } from "@file/xml-components"; import { XmlComponent } from "@file/xml-components";
import { bookmarkUniqueNumericId } from "@util/convenience-functions"; import { bookmarkUniqueNumericIdGen } from "@util/convenience-functions";
import { ParagraphChild } from "../paragraph"; import { ParagraphChild } from "../paragraph";
import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attributes"; import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attributes";
export class Bookmark { export class Bookmark {
private readonly bookmarkUniqueNumericId = bookmarkUniqueNumericIdGen();
public readonly start: BookmarkStart; public readonly start: BookmarkStart;
public readonly children: readonly ParagraphChild[]; public readonly children: readonly ParagraphChild[];
public readonly end: BookmarkEnd; public readonly end: BookmarkEnd;
public constructor(options: { readonly id: string; readonly children: readonly ParagraphChild[] }) { public constructor(options: { readonly id: string; readonly children: readonly ParagraphChild[] }) {
const linkId = bookmarkUniqueNumericId(); const linkId = this.bookmarkUniqueNumericId();
this.start = new BookmarkStart(options.id, linkId); this.start = new BookmarkStart(options.id, linkId);
this.children = options.children; this.children = options.children;

View File

@ -20,12 +20,12 @@ import { TextRun } from "./run";
describe("Paragraph", () => { describe("Paragraph", () => {
before(() => { before(() => {
stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id"); stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id");
stub(convenienceFunctions, "bookmarkUniqueNumericId").callsFake(() => -101); stub(convenienceFunctions, "bookmarkUniqueNumericIdGen").callsFake(() => () => -101);
}); });
after(() => { after(() => {
(convenienceFunctions.uniqueId as SinonStub).restore(); (convenienceFunctions.uniqueId as SinonStub).restore();
(convenienceFunctions.bookmarkUniqueNumericId as SinonStub).restore(); (convenienceFunctions.bookmarkUniqueNumericIdGen as SinonStub).restore();
}); });
describe("#constructor()", () => { describe("#constructor()", () => {

View File

@ -11,12 +11,10 @@ import { ImageRun } from "./image-run";
describe("ImageRun", () => { describe("ImageRun", () => {
before(() => { before(() => {
stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id"); stub(convenienceFunctions, "uniqueId").callsFake(() => "test-unique-id");
stub(convenienceFunctions, "docPropertiesUniqueNumericId").callsFake(() => 0);
}); });
after(() => { after(() => {
(convenienceFunctions.uniqueId as SinonStub).restore(); (convenienceFunctions.uniqueId as SinonStub).restore();
(convenienceFunctions.docPropertiesUniqueNumericId as SinonStub).restore();
}); });
describe("#constructor()", () => { describe("#constructor()", () => {
@ -126,7 +124,7 @@ describe("ImageRun", () => {
"wp:docPr": { "wp:docPr": {
_attr: { _attr: {
descr: "", descr: "",
id: 0, id: 1,
name: "", name: "",
title: "", title: "",
}, },
@ -378,7 +376,7 @@ describe("ImageRun", () => {
"wp:docPr": { "wp:docPr": {
_attr: { _attr: {
descr: "", descr: "",
id: 0, id: 1,
name: "", name: "",
title: "", title: "",
}, },
@ -634,7 +632,7 @@ describe("ImageRun", () => {
"wp:docPr": { "wp:docPr": {
_attr: { _attr: {
descr: "", descr: "",
id: 0, id: 1,
name: "", name: "",
title: "", title: "",
}, },
@ -893,7 +891,7 @@ describe("ImageRun", () => {
"wp:docPr": { "wp:docPr": {
_attr: { _attr: {
descr: "", descr: "",
id: 0, id: 1,
name: "", name: "",
title: "", title: "",
}, },

View File

@ -5,15 +5,21 @@ export const convertMillimetersToTwip = (millimeters: number): number => Math.fl
export const convertInchesToTwip = (inches: number): number => Math.floor(inches * 72 * 20); export const convertInchesToTwip = (inches: number): number => Math.floor(inches * 72 * 20);
export const uniqueNumericIdCreator = (initial = 0): (() => number) => { export type UniqueNumericIdCreator = () => number;
export const uniqueNumericIdCreator = (initial = 0): UniqueNumericIdCreator => {
let currentCount = initial; let currentCount = initial;
return () => ++currentCount; return () => ++currentCount;
}; };
export const abstractNumUniqueNumericId = uniqueNumericIdCreator(); export const abstractNumUniqueNumericIdGen = (): UniqueNumericIdCreator => uniqueNumericIdCreator();
export const concreteNumUniqueNumericId = uniqueNumericIdCreator(1); // Setting initial to 1 as we have numId = 1 for "default-bullet-numbering"
export const docPropertiesUniqueNumericId = uniqueNumericIdCreator(); // Setting initial to 1 as we have numId = 1 for "default-bullet-numbering"
export const bookmarkUniqueNumericId = uniqueNumericIdCreator(); export const concreteNumUniqueNumericIdGen = (): UniqueNumericIdCreator => uniqueNumericIdCreator(1);
export const docPropertiesUniqueNumericIdGen = (): UniqueNumericIdCreator => uniqueNumericIdCreator();
export const bookmarkUniqueNumericIdGen = (): UniqueNumericIdCreator => uniqueNumericIdCreator();
export const uniqueId = (): string => nanoid().toLowerCase(); export const uniqueId = (): string => nanoid().toLowerCase();