diff --git a/package-lock.json b/package-lock.json index dc227188..9b50fc2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,10 @@ "@rollup/plugin-commonjs": "^12.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.1.1", + "@rollup/plugin-typescript": "^11.0.0", + "@types/color-name": "^1.1.1", + "@types/lodash": "^4.14.186", + "@types/virtual-dom": "^2.1.1", "eslint": "^7.32.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-config-prettier": "^8.3.0", @@ -41,7 +45,8 @@ "rollup-plugin-cleaner": "^1.0.0", "rollup-plugin-node-builtins": "^2.1.2", "rollup-plugin-terser": "^7.0.2", - "standard-version": "^9.3.1" + "standard-version": "^9.3.1", + "typescript": "^4.8.4" } }, "node_modules/@babel/code-frame": { @@ -499,12 +504,20 @@ "dependencies": { "@oozcitak/infra": "1.0.5", "@oozcitak/url": "1.0.0", - "@oozcitak/util": "8.3.4" + "@oozcitak/util": "8.0.0" }, "engines": { "node": ">=8.0" } }, + "node_modules/@oozcitak/dom/node_modules/@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/@oozcitak/infra": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.5.tgz", @@ -516,6 +529,14 @@ "node": ">=6.0" } }, + "node_modules/@oozcitak/infra/node_modules/@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/@oozcitak/url": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.0.tgz", @@ -539,12 +560,28 @@ "node": ">=6.0" } }, + "node_modules/@oozcitak/url/node_modules/@oozcitak/infra/node_modules/@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@oozcitak/url/node_modules/@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/@oozcitak/util": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==", "engines": { - "node": ">=8.0" + "node": ">=6.0" } }, "node_modules/@rollup/plugin-commonjs": { @@ -600,6 +637,66 @@ "rollup": "^2.42.0" } }, + "node_modules/@rollup/plugin-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.0.0.tgz", + "integrity": "sha512-goPyCWBiimk1iJgSTgsehFD5OOFHiAknrRJjqFCudcW8JtWiBlK284Xnn4flqMqg6YAjVG/EE+3aVzrL5qNSzQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-typescript/node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-typescript/node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "node_modules/@rollup/plugin-typescript/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", @@ -617,6 +714,12 @@ "rollup": "^1.20.0||^2.0.0" } }, + "node_modules/@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "node_modules/@types/estree": { "version": "0.0.39", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", @@ -629,6 +732,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.189", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.189.tgz", + "integrity": "sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA==", + "dev": true + }, "node_modules/@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -662,6 +771,12 @@ "@types/node": "*" } }, + "node_modules/@types/virtual-dom": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/virtual-dom/-/virtual-dom-2.1.1.tgz", + "integrity": "sha512-BPy2A3WXWL9RMoQQ0UQueKX93N/NL9KmlyNiSklj+Sbn91unDLcIUS00diWzV32N9CD7rRUrXE9VIzqMAZ3Wcg==", + "dev": true + }, "node_modules/abstract-leveldown": { "version": "0.12.4", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz", @@ -1436,58 +1551,6 @@ "node": ">=4" } }, - "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/conventional-changelog-core/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/conventional-changelog-core/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/conventional-changelog-core/node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -1548,15 +1611,6 @@ "validate-npm-package-license": "^3.0.1" } }, - "node_modules/conventional-changelog-core/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, "node_modules/conventional-changelog-ember": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", @@ -2836,12 +2890,6 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "node_modules/foreach": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", - "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", - "dev": true - }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -2936,12 +2984,6 @@ "string_decoder": "~0.10.x" } }, - "node_modules/fwd-stream/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3523,12 +3565,6 @@ "node": ">=8" } }, - "node_modules/indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==", - "dev": true - }, "node_modules/individual": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/individual/-/individual-3.0.0.tgz", @@ -3569,15 +3605,6 @@ "node": ">= 0.4" } }, - "node_modules/is": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/is/-/is-0.2.7.tgz", - "integrity": "sha512-ajQCouIvkcSnl2iRdK70Jug9mohIHVX9uKpoWnl115ov0R5mzBvRrXxrnHbsA+8AdwCwc/sfw7HXmd4I5EJBdQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -4041,14 +4068,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -4133,12 +4152,6 @@ "xtend": "~2.1.2" } }, - "node_modules/level-js/node_modules/object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==", - "dev": true - }, "node_modules/level-js/node_modules/xtend": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", @@ -4172,12 +4185,6 @@ "xtend": "~2.0.4" } }, - "node_modules/level-sublevel/node_modules/is-object": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-0.1.2.tgz", - "integrity": "sha512-GkfZZlIZtpkFrqyAXPQSRBMsaHAw+CgoKe2HXAkjd/sfoI9+hS8PT4wg2rJxdQyUKr7N2vHJbg7/jQtE5l5vBQ==", - "dev": true - }, "node_modules/level-sublevel/node_modules/level-fix-range": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/level-fix-range/-/level-fix-range-2.0.0.tgz", @@ -4187,18 +4194,6 @@ "clone": "~0.1.9" } }, - "node_modules/level-sublevel/node_modules/object-keys": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.2.0.tgz", - "integrity": "sha512-XODjdR2pBh/1qrjPcbSeSgEtKbYo7LqYNq64/TPuCf7j9SfDD3i21yatKoIy39yIWNvVM59iutfQQpCv1RfFzA==", - "deprecated": "Please update to the latest object-keys", - "dev": true, - "dependencies": { - "foreach": "~2.0.1", - "indexof": "~0.0.1", - "is": "~0.2.6" - } - }, "node_modules/level-sublevel/node_modules/xtend": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.0.6.tgz", @@ -4982,15 +4977,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -5374,12 +5360,6 @@ "node": ">=8" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -5897,18 +5877,6 @@ "node": ">=10" } }, - "node_modules/standard-version/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/standard-version/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5923,62 +5891,6 @@ "node": ">=4" } }, - "node_modules/standard-version/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/standard-version/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/standard-version/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/standard-version/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/standard-version/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/standard-version/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/standard-version/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -7057,6 +6969,13 @@ "@oozcitak/infra": "1.0.5", "@oozcitak/url": "1.0.0", "@oozcitak/util": "8.3.4" + }, + "dependencies": { + "@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==" + } } }, "@oozcitak/infra": { @@ -7065,6 +6984,13 @@ "integrity": "sha512-o+zZH7M6l5e3FaAWy3ojaPIVN5eusaYPrKm6MZQt0DKNdgXa2wDYExjpP0t/zx+GoQgQKzLu7cfD8rHCLt8JrQ==", "requires": { "@oozcitak/util": "8.3.4" + }, + "dependencies": { + "@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==" + } } }, "@oozcitak/url": { @@ -7082,7 +7008,19 @@ "integrity": "sha512-9O2wxXGnRzy76O1XUxESxDGsXT5kzETJPvYbreO4mv6bqe1+YSuux2cZTagjJ/T4UfEwFJz5ixanOqB0QgYAag==", "requires": { "@oozcitak/util": "8.3.4" + }, + "dependencies": { + "@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==" + } } + }, + "@oozcitak/util": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.4.tgz", + "integrity": "sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==" } } }, @@ -7129,6 +7067,41 @@ "resolve": "^1.19.0" } }, + "@rollup/plugin-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.0.0.tgz", + "integrity": "sha512-goPyCWBiimk1iJgSTgsehFD5OOFHiAknrRJjqFCudcW8JtWiBlK284Xnn4flqMqg6YAjVG/EE+3aVzrL5qNSzQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "resolve": "^1.22.1" + }, + "dependencies": { + "@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + } + } + }, "@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", @@ -7140,6 +7113,12 @@ "picomatch": "^2.2.2" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/estree": { "version": "0.0.39", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", @@ -7152,6 +7131,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/lodash": { + "version": "4.14.189", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.189.tgz", + "integrity": "sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA==", + "dev": true + }, "@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -7185,6 +7170,12 @@ "@types/node": "*" } }, + "@types/virtual-dom": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/virtual-dom/-/virtual-dom-2.1.1.tgz", + "integrity": "sha512-BPy2A3WXWL9RMoQQ0UQueKX93N/NL9KmlyNiSklj+Sbn91unDLcIUS00diWzV32N9CD7rRUrXE9VIzqMAZ3Wcg==", + "dev": true + }, "abstract-leveldown": { "version": "0.12.4", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz", @@ -7828,46 +7819,6 @@ "locate-path": "^2.0.0" } }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -7917,12 +7868,6 @@ "find-up": "^2.0.0", "read-pkg": "^3.0.0" } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true } } }, @@ -8946,12 +8891,6 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "foreach": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", - "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", - "dev": true - }, "fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -9026,12 +8965,6 @@ "isarray": "0.0.1", "string_decoder": "~0.10.x" } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true } } }, @@ -9479,12 +9412,6 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==", - "dev": true - }, "individual": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/individual/-/individual-3.0.0.tgz", @@ -9522,12 +9449,6 @@ "side-channel": "^1.0.4" } }, - "is": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/is/-/is-0.2.7.tgz", - "integrity": "sha512-ajQCouIvkcSnl2iRdK70Jug9mohIHVX9uKpoWnl115ov0R5mzBvRrXxrnHbsA+8AdwCwc/sfw7HXmd4I5EJBdQ==", - "dev": true - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -9871,14 +9792,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } } } }, @@ -9965,12 +9878,6 @@ "xtend": "~2.1.2" }, "dependencies": { - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==", - "dev": true - }, "xtend": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", @@ -10003,12 +9910,6 @@ "xtend": "~2.0.4" }, "dependencies": { - "is-object": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-0.1.2.tgz", - "integrity": "sha512-GkfZZlIZtpkFrqyAXPQSRBMsaHAw+CgoKe2HXAkjd/sfoI9+hS8PT4wg2rJxdQyUKr7N2vHJbg7/jQtE5l5vBQ==", - "dev": true - }, "level-fix-range": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/level-fix-range/-/level-fix-range-2.0.0.tgz", @@ -10018,17 +9919,6 @@ "clone": "~0.1.9" } }, - "object-keys": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.2.0.tgz", - "integrity": "sha512-XODjdR2pBh/1qrjPcbSeSgEtKbYo7LqYNq64/TPuCf7j9SfDD3i21yatKoIy39yIWNvVM59iutfQQpCv1RfFzA==", - "dev": true, - "requires": { - "foreach": "~2.0.1", - "indexof": "~0.0.1", - "is": "~0.2.6" - } - }, "xtend": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.0.6.tgz", @@ -10638,12 +10528,6 @@ "aggregate-error": "^3.0.0" } }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -10875,12 +10759,6 @@ "type-fest": "^0.6.0" }, "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -11345,15 +11223,6 @@ "yargs": "^16.0.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -11365,53 +11234,6 @@ "supports-color": "^5.3.0" } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index eb8f093d..c842ebe5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ ], "main": "dist/html-to-docx.umd.js", "module": "dist/html-to-docx.esm.js", + "types": "./dist/index.d.ts", "scripts": { "test": "npm run build && node example/example-node.js", "prerelease": "rollup -c", @@ -50,6 +51,10 @@ "@rollup/plugin-commonjs": "^12.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.1.1", + "@rollup/plugin-typescript": "^11.0.0", + "@types/color-name": "^1.1.1", + "@types/lodash": "^4.14.186", + "@types/virtual-dom": "^2.1.1", "eslint": "^7.32.0", "eslint-config-airbnb-base": "^14.2.1", "eslint-config-prettier": "^8.3.0", @@ -62,11 +67,12 @@ "rollup-plugin-cleaner": "^1.0.0", "rollup-plugin-node-builtins": "^2.1.2", "rollup-plugin-terser": "^7.0.2", - "standard-version": "^9.3.1" + "standard-version": "^9.3.1", + "typescript": "^4.8.4" }, "dependencies": { - "@oozcitak/util": "8.3.4", "@oozcitak/dom": "1.15.6", + "@oozcitak/util": "8.3.4", "color-name": "^1.1.4", "html-entities": "^2.3.3", "html-to-vdom": "^0.7.0", @@ -93,4 +99,4 @@ "@oozcitak/util": "8.3.4", "@oozcitak/dom": "1.15.6" } -} \ No newline at end of file +} diff --git a/rollup.config.js b/rollup.config.js index 7bb857aa..cac0b2c4 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,13 +4,15 @@ import commonjs from '@rollup/plugin-commonjs'; import { terser } from 'rollup-plugin-terser'; import cleaner from 'rollup-plugin-cleaner'; import builtins from 'rollup-plugin-node-builtins'; +import typescript from '@rollup/plugin-typescript'; import * as meta from './package.json'; export default { - input: 'index.js', + input: 'src/index.ts', external: ['color-name', 'html-to-vdom', 'jszip', 'virtual-dom', 'xmlbuilder2', 'html-entities'], plugins: [ + typescript(), resolve(), json(), commonjs(), diff --git a/src/constants.js b/src/constants.ts similarity index 100% rename from src/constants.js rename to src/constants.ts diff --git a/src/docx-document.js b/src/docx-document.ts similarity index 83% rename from src/docx-document.js rename to src/docx-document.ts index 1a3168b3..338c1bf9 100644 --- a/src/docx-document.js +++ b/src/docx-document.ts @@ -1,5 +1,8 @@ import { create, fragment } from 'xmlbuilder2'; import { nanoid } from 'nanoid'; +import { XMLBuilder } from 'xmlbuilder2/lib/interfaces'; +import { VTree } from 'virtual-dom'; +import JSZip from 'jszip'; import { generateCoreXML, @@ -34,8 +37,13 @@ import { defaultDocumentOptions, } from './constants'; import ListStyleBuilder from './utils/list'; +import { DocumentOptions, LineNumberOptions, Margins, PageSize } from './interface'; -function generateContentTypesFragments(contentTypesXML, type, objects) { +function generateContentTypesFragments( + contentTypesXML: XMLBuilder, + type: 'header' | 'footer', + objects: Object[] +) { if (objects && Array.isArray(objects)) { objects.forEach((object) => { const contentTypesFragment = fragment({ defaultNamespace: { ele: namespaces.contentTypes } }) @@ -52,7 +60,12 @@ function generateContentTypesFragments(contentTypesXML, type, objects) { } } -function generateSectionReferenceXML(documentXML, documentSectionType, objects, isEnabled) { +function generateSectionReferenceXML( + documentXML: XMLBuilder, + documentSectionType: 'header' | 'footer', + objects: { relationshipId: string; type: string }[], + isEnabled: boolean +) { if (isEnabled && objects && Array.isArray(objects) && objects.length) { const xmlFragment = fragment(); objects.forEach(({ relationshipId, type }) => { @@ -68,12 +81,12 @@ function generateSectionReferenceXML(documentXML, documentSectionType, objects, } } -function generateXMLString(xmlString) { +function generateXMLString(xmlString: string) { const xmlDocumentString = create({ encoding: 'UTF-8', standalone: true }, xmlString); return xmlDocumentString.toString({ prettyPrint: true }); } -async function generateSectionXML(vTree, type = 'header') { +async function generateSectionXML(vTree: VTree, type: 'header' | 'footer' = 'header') { const sectionXML = create({ encoding: 'UTF-8', standalone: true, @@ -90,6 +103,7 @@ async function generateSectionXML(vTree, type = 'header') { const XMLFragment = fragment(); await convertVTreeToXML(this, vTree, XMLFragment); + // @ts-ignore if (type === 'footer' && XMLFragment.first().node.tagName === 'p' && this.pageNumber) { XMLFragment.first().import( fragment({ namespaceAlias: { w: namespaces.w } }) @@ -108,7 +122,58 @@ async function generateSectionXML(vTree, type = 'header') { return { [`${type}Id`]: this[`last${referenceName}Id`], [`${type}XML`]: sectionXML }; } +interface IRelationship { + relationshipId: string; + type: 'header' | 'footer' | 'image' | 'hyperlink' | 'theme'; + target: string; + targetMode: 'External' | 'Internal'; +} + class DocxDocument { + zip: JSZip; + htmlString: string; + orientation: DocumentOptions['orientation']; + pageSize: PageSize; + width: number; + height: number; + margins: Margins; + availableDocumentSpace: number; + title: string; + subject: string; + creator: string; + keywords: string[]; + description: string; + lastModifiedBy: string; + revision: number; + createdAt: Date; + modifiedAt: Date; + headerType: DocumentOptions['headerType']; + header: boolean; + footerType: DocumentOptions['footerType']; + footer: boolean; + font: string; + fontSize: number; + complexScriptFontSize: number; + tableRowCantSplit: boolean; + pageNumber: boolean; + skipFirstHeaderFooter: boolean; + lineNumber: LineNumberOptions | null; + lastNumberingId: number; + lastMediaId: number; + lastHeaderId: number; + lastFooterId: number; + // FIXME: + numberingObjects: any[]; + // FIXME: + headerObjects: any[]; + // FIXME: + footerObjects: any[]; + relationshipFilename: string; + relationships: { fileName: string; lastRelsId: number; rels: IRelationship[] }[]; + documentXML: XMLBuilder | null; + generateSectionXML: Function; + ListStyleBuilder: typeof ListStyleBuilder; + constructor(properties) { this.zip = properties.zip; this.htmlString = properties.htmlString; @@ -158,11 +223,9 @@ class DocxDocument { this.lastMediaId = 0; this.lastHeaderId = 0; this.lastFooterId = 0; - this.stylesObjects = []; this.numberingObjects = []; this.relationshipFilename = documentFileName; this.relationships = [{ fileName: documentFileName, lastRelsId: 4, rels: [] }]; - this.mediaFiles = []; this.headerObjects = []; this.footerObjects = []; this.documentXML = null; @@ -183,6 +246,7 @@ class DocxDocument { this.generateFooterXML = this.generateFooterXML.bind(this); this.generateSectionXML = generateSectionXML.bind(this); + // @ts-ignore this.ListStyleBuilder = new ListStyleBuilder(properties.numbering); } @@ -200,7 +264,7 @@ class DocxDocument { { encoding: 'UTF-8', standalone: true }, generateDocumentTemplate(this.width, this.height, this.orientation, this.margins) ); - documentXML.root().first().import(this.documentXML); + documentXML.root().first().import(this.documentXML!); generateSectionReferenceXML(documentXML, 'header', this.headerObjects, this.header); generateSectionReferenceXML(documentXML, 'footer', this.footerObjects, this.footer); @@ -221,8 +285,8 @@ class DocxDocument { .import( fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'lnNumType') - .att('@w', 'countBy', countBy) - .att('@w', 'start', start) + .att('@w', 'countBy', String(countBy)) + .att('@w', 'start', String(start)) .att('@w', 'restart', restart) ); } @@ -288,7 +352,7 @@ class DocxDocument { [...Array(8).keys()].forEach((level) => { const levelFragment = fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'lvl') - .att('@w', 'ilvl', level) + .att('@w', 'ilvl', String(level)) .ele('@w', 'start') .att( '@w', @@ -303,7 +367,8 @@ class DocxDocument { '@w', 'val', type === 'ol' - ? this.ListStyleBuilder.getListStyleType( + ? // @ts-ignore + this.ListStyleBuilder.getListStyleType( properties.style && properties.style['list-style-type'] ) : 'bullet' @@ -313,6 +378,7 @@ class DocxDocument { .att( '@w', 'val', + // @ts-ignore type === 'ol' ? this.ListStyleBuilder.getListPrefixSuffix(properties.style, level) : '' ) .up() @@ -323,12 +389,12 @@ class DocxDocument { .ele('@w', 'tabs') .ele('@w', 'tab') .att('@w', 'val', 'num') - .att('@w', 'pos', (level + 1) * 720) + .att('@w', 'pos', String((level + 1) * 720)) .up() .up() .ele('@w', 'ind') - .att('@w', 'left', (level + 1) * 720) - .att('@w', 'hanging', 360) + .att('@w', 'left', String((level + 1) * 720)) + .att('@w', 'hanging', String(360)) .up() .up() .up(); @@ -368,7 +434,7 @@ class DocxDocument { } // eslint-disable-next-line class-methods-use-this - appendRelationships(xmlFragment, relationships) { + appendRelationships(xmlFragment: XMLBuilder, relationships: IRelationship[]) { relationships.forEach(({ relationshipId, type, target, targetMode }) => { xmlFragment.import( fragment({ defaultNamespace: { ele: namespaces.relationship } }) @@ -396,16 +462,16 @@ class DocxDocument { return relationshipXMLStrings; } - createNumbering(type, properties) { + createNumbering(type: string, properties: Object) { this.lastNumberingId += 1; this.numberingObjects.push({ numberingId: this.lastNumberingId, type, properties }); return this.lastNumberingId; } - createMediaFile(base64String) { + createMediaFile(base64String: string) { // eslint-disable-next-line no-useless-escape - const matches = base64String.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/); + const matches = base64String.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/) as RegExpMatchArray; if (matches.length !== 3) { throw new Error('Invalid base64 string'); } @@ -413,6 +479,7 @@ class DocxDocument { const base64FileContent = matches[2]; // matches array contains file type in base64 format - image/jpeg and base64 stringified data const fileExtension = + // @ts-ignore matches[1].match(/\/(.*?)$/)[1] === 'octet-stream' ? 'png' : matches[1].match(/\/(.*?)$/)[1]; const fileNameWithExtension = `image-${nanoid()}.${fileExtension}`; @@ -422,10 +489,15 @@ class DocxDocument { return { id: this.lastMediaId, fileContent: base64FileContent, fileNameWithExtension }; } - createDocumentRelationships(fileName = 'document', type, target, targetMode = 'External') { + createDocumentRelationships( + fileName: string = 'document', + type: IRelationship['type'], + target: string, + targetMode: IRelationship['targetMode'] = 'External' + ) { let relationshipObject = this.relationships.find( (relationship) => relationship.fileName === fileName - ); + )!; let lastRelsId = 1; if (relationshipObject) { lastRelsId = relationshipObject.lastRelsId + 1; @@ -454,6 +526,7 @@ class DocxDocument { } relationshipObject.rels.push({ + // @ts-ignore relationshipId: lastRelsId, type: relationshipType, target, @@ -463,11 +536,11 @@ class DocxDocument { return lastRelsId; } - generateHeaderXML(vTree) { + generateHeaderXML(vTree: VTree) { return this.generateSectionXML(vTree, 'header'); } - generateFooterXML(vTree) { + generateFooterXML(vTree: VTree) { return this.generateSectionXML(vTree, 'footer'); } } diff --git a/src/helpers/index.js b/src/helpers/index.ts similarity index 72% rename from src/helpers/index.js rename to src/helpers/index.ts index a9ce16ef..c5d26026 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.ts @@ -1,3 +1,2 @@ -/* eslint-disable import/prefer-default-export */ export { default as renderDocumentFile } from './render-document-file'; export { convertVTreeToXML } from './render-document-file'; diff --git a/src/helpers/render-document-file.js b/src/helpers/render-document-file.ts similarity index 93% rename from src/helpers/render-document-file.js rename to src/helpers/render-document-file.ts index 507c1767..816202af 100644 --- a/src/helpers/render-document-file.js +++ b/src/helpers/render-document-file.ts @@ -10,6 +10,8 @@ import { default as HTMLToVDOM } from 'html-to-vdom'; import sizeOf from 'image-size'; import imageToBase64 from 'image-to-base64'; import mimeTypes from 'mime-types'; +import { VNode as VNodeType, VTree } from 'virtual-dom'; +import { XMLBuilder } from 'xmlbuilder2/lib/interfaces'; // FIXME: remove the cyclic dependency // eslint-disable-next-line import/no-cycle @@ -25,15 +27,15 @@ const convertHTML = HTMLToVDOM({ }); // eslint-disable-next-line consistent-return, no-shadow -export const buildImage = async (docxDocumentInstance, vNode, maximumWidth = null) => { - let response = null; - let base64Uri = null; +export const buildImage = async (docxDocumentInstance, vNode: VNodeType, maximumWidth = null) => { + let response: { id: number; fileContent: string; fileNameWithExtension: string } | null = null; + let base64Uri: string | null = null; try { const imageSource = vNode.properties.src; if (isValidUrl(imageSource)) { const base64String = await imageToBase64(imageSource).catch((error) => { // eslint-disable-next-line no-console - console.warning(`skipping image download and conversion due to ${error}`); + console.warn(`skipping image download and conversion due to ${error}`); }); if (base64String) { @@ -85,7 +87,11 @@ export const buildImage = async (docxDocumentInstance, vNode, maximumWidth = nul } }; -export const buildList = async (vNode, docxDocumentInstance, xmlFragment) => { +export const buildList = async ( + vNode: VNodeType, + docxDocumentInstance, + xmlFragment: XMLBuilder +) => { const listElements = []; let vNodeObjects = [ @@ -179,7 +185,7 @@ export const buildList = async (vNode, docxDocumentInstance, xmlFragment) => { return listElements; }; -async function findXMLEquivalent(docxDocumentInstance, vNode, xmlFragment) { +async function findXMLEquivalent(docxDocumentInstance, vNode: VNodeType, xmlFragment: XMLBuilder) { if ( vNode.tagName === 'div' && (vNode.properties.attributes.class === 'page-break' || @@ -302,7 +308,11 @@ async function findXMLEquivalent(docxDocumentInstance, vNode, xmlFragment) { } // eslint-disable-next-line consistent-return -export async function convertVTreeToXML(docxDocumentInstance, vTree, xmlFragment) { +export async function convertVTreeToXML( + docxDocumentInstance, + vTree: VTree, + xmlFragment: XMLBuilder +) { if (!vTree) { // eslint-disable-next-line no-useless-return return ''; diff --git a/src/helpers/xml-builder.js b/src/helpers/xml-builder.ts similarity index 89% rename from src/helpers/xml-builder.js rename to src/helpers/xml-builder.ts index 1a311ca8..aa78d6d7 100644 --- a/src/helpers/xml-builder.js +++ b/src/helpers/xml-builder.ts @@ -7,11 +7,13 @@ import { fragment } from 'xmlbuilder2'; import isVNode from 'virtual-dom/vnode/is-vnode'; import isVText from 'virtual-dom/vnode/is-vtext'; -import colorNames from 'color-name'; +import colorNames, { RGB } from 'color-name'; import { cloneDeep } from 'lodash'; import imageToBase64 from 'image-to-base64'; import mimeTypes from 'mime-types'; import sizeOf from 'image-size'; +import { VNode } from 'virtual-dom'; +import { XMLBuilder } from 'xmlbuilder2/lib/interfaces'; import namespaces from '../namespaces'; import { @@ -56,32 +58,51 @@ import { import { vNodeHasChildren } from '../utils/vnode'; import { isValidUrl } from '../utils/url'; -// eslint-disable-next-line consistent-return -const fixupColorCode = (colorCodeString) => { - if (Object.prototype.hasOwnProperty.call(colorNames, colorCodeString.toLowerCase())) { - const [red, green, blue] = colorNames[colorCodeString.toLowerCase()]; +export type Attributes = Partial< + Record< + | 'strong' + | 'i' + | 'u' + | 'sub' + | 'sup' + | 'color' + | 'backgroundColor' + | 'fontSize' + | 'hyperlink' + | 'highlightColor' + | 'font' + | 'verticalAlign' + | 'textAlign' + | 'lineHeight', + unknown + > +>; + +const fixupColorCode = (colorCode: string) => { + if (Object.prototype.hasOwnProperty.call(colorNames, colorCode.toLowerCase())) { + const [red, green, blue]: RGB = colorNames[colorCode.toLowerCase()]; return rgbToHex(red, green, blue); - } else if (rgbRegex.test(colorCodeString)) { - const matchedParts = colorCodeString.match(rgbRegex); + } else if (rgbRegex.test(colorCode)) { + const matchedParts = colorCode.match(rgbRegex) as RegExpMatchArray; const red = matchedParts[1]; const green = matchedParts[2]; const blue = matchedParts[3]; return rgbToHex(red, green, blue); - } else if (hslRegex.test(colorCodeString)) { - const matchedParts = colorCodeString.match(hslRegex); + } else if (hslRegex.test(colorCode)) { + const matchedParts = colorCode.match(hslRegex) as RegExpMatchArray; const hue = matchedParts[1]; const saturation = matchedParts[2]; const luminosity = matchedParts[3]; return hslToHex(hue, saturation, luminosity); - } else if (hexRegex.test(colorCodeString)) { - const matchedParts = colorCodeString.match(hexRegex); + } else if (hexRegex.test(colorCode)) { + const matchedParts = colorCode.match(hexRegex) as RegExpMatchArray; return matchedParts[1]; - } else if (hex3Regex.test(colorCodeString)) { - const matchedParts = colorCodeString.match(hex3Regex); + } else if (hex3Regex.test(colorCode)) { + const matchedParts = colorCode.match(hex3Regex) as RegExpMatchArray; const red = matchedParts[1]; const green = matchedParts[2]; const blue = matchedParts[3]; @@ -105,14 +126,14 @@ const buildRunStyleFragment = (type = 'Hyperlink') => .att('@w', 'val', type) .up(); -const buildTableRowHeight = (tableRowHeight) => +const buildTableRowHeight = (tableRowHeight: string | number) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'trHeight') - .att('@w', 'val', tableRowHeight) + .att('@w', 'val', tableRowHeight as string) .att('@w', 'hRule', 'atLeast') .up(); -const buildVerticalAlignment = (verticalAlignment) => { +const buildVerticalAlignment = (verticalAlignment: 'both' | 'bottom' | 'center' | 'top') => { if (verticalAlignment.toLowerCase() === 'middle') { verticalAlignment = 'center'; } @@ -123,25 +144,25 @@ const buildVerticalAlignment = (verticalAlignment) => { .up(); }; -const buildVerticalMerge = (verticalMerge = 'continue') => +const buildVerticalMerge = (verticalMerge: 'continue' | 'restart' = 'continue') => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'vMerge') .att('@w', 'val', verticalMerge) .up(); -const buildColor = (colorCode) => +const buildColor = (colorCode: string) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'color') .att('@w', 'val', colorCode) .up(); -const buildFontSize = (fontSize) => +const buildFontSize = (fontSize: string | number) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'sz') - .att('@w', 'val', fontSize) + .att('@w', 'val', fontSize as string) .up(); -const buildShading = (colorCode) => +const buildShading = (colorCode: string) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'shd') .att('@w', 'val', 'clear') @@ -154,7 +175,7 @@ const buildHighlight = (color = 'yellow') => .att('@w', 'val', color) .up(); -const buildVertAlign = (type = 'baseline') => +const buildVertAlign = (type: 'baseline' | 'subscript' | 'superscript' = 'baseline') => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'vertAlign') .att('@w', 'val', type) @@ -163,7 +184,7 @@ const buildVertAlign = (type = 'baseline') => const buildStrike = () => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'strike') - .att('@w', 'val', true) + .att('@w', 'val', 'true') .up(); const buildBold = () => @@ -176,18 +197,21 @@ const buildItalics = () => .ele('@w', 'i') .up(); -const buildUnderline = (type = 'single') => +const buildUnderline = ( + type: 'double' | 'doubleAccounting' | 'none' | 'single' | 'singleAccounting' = 'single' +) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'u') .att('@w', 'val', type) .up(); -const buildLineBreak = (type = 'textWrapping') => +const buildLineBreak = (type: 'column' | 'page' | 'textWrapping' = 'textWrapping') => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'br') .att('@w', 'type', type) .up(); +// TODO: type buildBorder const buildBorder = ( borderSide = 'top', borderSize = 0, @@ -198,12 +222,12 @@ const buildBorder = ( fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', borderSide) .att('@w', 'val', borderStroke) - .att('@w', 'sz', borderSize) - .att('@w', 'space', borderSpacing) + .att('@w', 'sz', String(borderSize)) + .att('@w', 'space', String(borderSpacing)) .att('@w', 'color', borderColor) .up(); -const buildTextElement = (text) => +const buildTextElement = (text: string) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 't') .att('@xml', 'space', 'preserve') @@ -211,10 +235,10 @@ const buildTextElement = (text) => .up(); // eslint-disable-next-line consistent-return -const fixupLineHeight = (lineHeight, fontSize) => { +const fixupLineHeight = (lineHeight?: unknown, fontSize?: number) => { // FIXME: If line height is anything other than a number // eslint-disable-next-line no-restricted-globals - if (!isNaN(lineHeight)) { + if (lineHeight && !isNaN(lineHeight as number)) { if (fontSize) { const actualLineHeight = +lineHeight * fontSize; @@ -230,68 +254,62 @@ const fixupLineHeight = (lineHeight, fontSize) => { }; // eslint-disable-next-line consistent-return -const fixupFontSize = (fontSizeString) => { - if (pointRegex.test(fontSizeString)) { - const matchedParts = fontSizeString.match(pointRegex); - // convert point to half point - return pointToHIP(matchedParts[1]); - } else if (pixelRegex.test(fontSizeString)) { - const matchedParts = fontSizeString.match(pixelRegex); - // convert pixels to half point - return pixelToHIP(matchedParts[1]); +const fixupFontSize = (fontSize: string) => { + if (pointRegex.test(fontSize)) { + const matchedParts = fontSize.match(pointRegex) as string[]; + return pointToHIP(parseFloat(matchedParts[1])); + } else if (pixelRegex.test(fontSize)) { + const matchedParts = fontSize.match(pixelRegex) as string[]; + return pixelToHIP(parseFloat(matchedParts[1])); } }; // eslint-disable-next-line consistent-return -const fixupRowHeight = (rowHeightString) => { +const fixupRowHeight = (rowHeightString: string) => { if (pointRegex.test(rowHeightString)) { - const matchedParts = rowHeightString.match(pointRegex); - // convert point to half point - return pointToTWIP(matchedParts[1]); + const matchedParts = rowHeightString.match(pointRegex) as string[]; + return pointToTWIP(parseFloat(matchedParts[1])); } else if (pixelRegex.test(rowHeightString)) { - const matchedParts = rowHeightString.match(pixelRegex); - // convert pixels to half point - return pixelToTWIP(matchedParts[1]); + const matchedParts = rowHeightString.match(pixelRegex) as string[]; + return pixelToTWIP(parseFloat(matchedParts[1])); } else if (cmRegex.test(rowHeightString)) { - const matchedParts = rowHeightString.match(cmRegex); - return cmToTWIP(matchedParts[1]); + const matchedParts = rowHeightString.match(cmRegex) as string[]; + return cmToTWIP(parseFloat(matchedParts[1])); } else if (inchRegex.test(rowHeightString)) { - const matchedParts = rowHeightString.match(inchRegex); - return inchToTWIP(matchedParts[1]); + const matchedParts = rowHeightString.match(inchRegex) as string[]; + return inchToTWIP(parseFloat(matchedParts[1])); } }; // eslint-disable-next-line consistent-return const fixupColumnWidth = (columnWidthString) => { if (pointRegex.test(columnWidthString)) { - const matchedParts = columnWidthString.match(pointRegex); - return pointToTWIP(matchedParts[1]); + const matchedParts = columnWidthString.match(pointRegex) as string[]; + return pointToTWIP(parseFloat(matchedParts[1])); } else if (pixelRegex.test(columnWidthString)) { - const matchedParts = columnWidthString.match(pixelRegex); - return pixelToTWIP(matchedParts[1]); + const matchedParts = columnWidthString.match(pixelRegex) as string[]; + return pixelToTWIP(parseFloat(matchedParts[1])); } else if (cmRegex.test(columnWidthString)) { - const matchedParts = columnWidthString.match(cmRegex); - return cmToTWIP(matchedParts[1]); + const matchedParts = columnWidthString.match(cmRegex) as string[]; + return cmToTWIP(parseFloat(matchedParts[1])); } else if (inchRegex.test(columnWidthString)) { - const matchedParts = columnWidthString.match(inchRegex); - return inchToTWIP(matchedParts[1]); + const matchedParts = columnWidthString.match(inchRegex) as string[]; + return inchToTWIP(parseFloat(matchedParts[1])); } }; // eslint-disable-next-line consistent-return -const fixupMargin = (marginString) => { - if (pointRegex.test(marginString)) { - const matchedParts = marginString.match(pointRegex); - // convert point to half point - return pointToTWIP(matchedParts[1]); - } else if (pixelRegex.test(marginString)) { - const matchedParts = marginString.match(pixelRegex); - // convert pixels to half point - return pixelToTWIP(matchedParts[1]); +const fixupMargin = (margin: string) => { + if (pointRegex.test(margin)) { + const matchedParts = margin.match(pointRegex) as string[]; + return pointToTWIP(parseFloat(matchedParts[1])); + } else if (pixelRegex.test(margin)) { + const matchedParts = margin.match(pixelRegex) as string[]; + return pixelToTWIP(parseFloat(matchedParts[1])); } }; -const modifiedStyleAttributesBuilder = (vNode, attributes, options) => { +const modifiedStyleAttributesBuilder = (vNode: VNode, attributes: Attributes, options) => { const modifiedAttributes = { ...attributes }; // styles @@ -420,7 +438,7 @@ const buildFormatting = (htmlTag, options) => { return null; }; -const buildRunProperties = (attributes) => { +const buildRunProperties = (attributes: Attributes) => { const runPropertiesFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele('@w', 'rPr'); if (attributes && attributes.constructor === Object) { Object.keys(attributes).forEach((key) => { @@ -444,7 +462,7 @@ const buildRunProperties = (attributes) => { return runPropertiesFragment; }; -const buildRun = async (vNode, attributes, docxDocumentInstance) => { +const buildRun = async (vNode: VNode, attributes: Attributes, docxDocumentInstance) => { const runFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele('@w', 'r'); const runPropertiesFragment = buildRunProperties(cloneDeep(attributes)); @@ -474,14 +492,14 @@ const buildRun = async (vNode, attributes, docxDocumentInstance) => { 'pre', ].includes(vNode.tagName) ) { - const runFragmentsArray = []; + const runFragmentsArray: XMLBuilder[] = []; let vNodes = [vNode]; // create temp run fragments to split the paragraph into different runs let tempAttributes = cloneDeep(attributes); let tempRunFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele('@w', 'r'); while (vNodes.length) { - const tempVNode = vNodes.shift(); + const tempVNode = vNodes.shift() as VNode; if (isVText(tempVNode)) { const textFragment = buildTextElement(tempVNode.text); const tempRunPropertiesFragment = buildRunProperties({ ...attributes, ...tempAttributes }); @@ -618,9 +636,9 @@ const buildRun = async (vNode, attributes, docxDocumentInstance) => { return runFragment; }; -const buildRunOrRuns = async (vNode, attributes, docxDocumentInstance) => { +const buildRunOrRuns = async (vNode: VNode, attributes: Attributes, docxDocumentInstance) => { if (isVNode(vNode) && vNode.tagName === 'span') { - let runFragments = []; + let runFragments: XMLBuilder[] = []; for (let index = 0; index < vNode.children.length; index++) { const childVNode = vNode.children[index]; @@ -638,7 +656,7 @@ const buildRunOrRuns = async (vNode, attributes, docxDocumentInstance) => { } }; -const buildRunOrHyperLink = async (vNode, attributes, docxDocumentInstance) => { +const buildRunOrHyperLink = async (vNode: VNode, attributes: Attributes, docxDocumentInstance) => { if (isVNode(vNode) && vNode.tagName === 'a') { const relationshipId = docxDocumentInstance.createDocumentRelationships( docxDocumentInstance.relationshipFilename, @@ -676,7 +694,7 @@ const buildRunOrHyperLink = async (vNode, attributes, docxDocumentInstance) => { return runFragments; }; -const buildNumberingProperties = (levelId, numberingId) => +const buildNumberingProperties = (levelId: number, numberingId: number) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'numPr') .ele('@w', 'ilvl') @@ -694,17 +712,17 @@ const buildNumberingInstances = () => .up() .up(); -const buildSpacing = (lineSpacing, beforeSpacing, afterSpacing) => { +const buildSpacing = (lineSpacing: number, beforeSpacing: number, afterSpacing: number) => { const spacingFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele('@w', 'spacing'); if (lineSpacing) { - spacingFragment.att('@w', 'line', lineSpacing); + spacingFragment.att('@w', 'line', String(lineSpacing)); } if (beforeSpacing) { - spacingFragment.att('@w', 'before', beforeSpacing); + spacingFragment.att('@w', 'before', String(beforeSpacing)); } if (afterSpacing) { - spacingFragment.att('@w', 'after', afterSpacing); + spacingFragment.att('@w', 'after', String(afterSpacing)); } spacingFragment.att('@w', 'lineRule', 'auto').up(); @@ -712,16 +730,20 @@ const buildSpacing = (lineSpacing, beforeSpacing, afterSpacing) => { return spacingFragment; }; -const buildIndentation = ({ left, right }) => { +interface IIndentation { + left: number; + right: number; +} +const buildIndentation = ({ left, right }: IIndentation) => { const indentationFragment = fragment({ namespaceAlias: { w: namespaces.w }, }).ele('@w', 'ind'); if (left) { - indentationFragment.att('@w', 'left', left); + indentationFragment.att('@w', 'left', String(left)); } if (right) { - indentationFragment.att('@w', 'right', right); + indentationFragment.att('@w', 'right', String(right)); } indentationFragment.up(); @@ -735,7 +757,9 @@ const buildPStyle = (style = 'Normal') => .att('@w', 'val', style) .up(); -const buildHorizontalAlignment = (horizontalAlignment) => { +const buildHorizontalAlignment = ( + horizontalAlignment: 'both' | 'center' | 'distribute' | 'highKashida' | 'justify' +) => { if (horizontalAlignment === 'justify') { horizontalAlignment = 'both'; } @@ -752,7 +776,7 @@ const buildParagraphBorder = () => { ); const bordersObject = cloneDeep(paragraphBordersObject); - Object.keys(bordersObject).forEach((borderName) => { + (Object.keys(bordersObject) as (keyof typeof bordersObject)[]).forEach((borderName) => { if (bordersObject[borderName]) { const { size, spacing, color } = bordersObject[borderName]; @@ -766,13 +790,14 @@ const buildParagraphBorder = () => { return paragraphBorderFragment; }; +// TODO: type attributes const buildParagraphProperties = (attributes) => { const paragraphPropertiesFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele( '@w', 'pPr' ); if (attributes && attributes.constructor === Object) { - Object.keys(attributes).forEach((key) => { + (Object.keys(attributes) as (keyof typeof attributes)[]).forEach((key) => { switch (key) { case 'numbering': const { levelId, numberingId } = attributes[key]; @@ -833,7 +858,8 @@ const buildParagraphProperties = (attributes) => { return paragraphPropertiesFragment; }; -const computeImageDimensions = (vNode, attributes) => { +// TODO: type attributes +const computeImageDimensions = (vNode: VNode, attributes) => { const { maximumWidth, originalWidth, originalHeight } = attributes; const aspectRatio = originalWidth / originalHeight; const maximumWidthInEMU = TWIPToEMU(maximumWidth); @@ -904,7 +930,7 @@ const computeImageDimensions = (vNode, attributes) => { attributes.height = modifiedHeight; }; -const buildParagraph = async (vNode, attributes, docxDocumentInstance) => { +const buildParagraph = async (vNode: VNode, attributes, docxDocumentInstance) => { const paragraphFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele('@w', 'p'); const modifiedAttributes = modifiedStyleAttributesBuilder(vNode, attributes, { isParagraph: true, @@ -968,7 +994,7 @@ const buildParagraph = async (vNode, attributes, docxDocumentInstance) => { if (isValidUrl(imageSource)) { base64String = await imageToBase64(imageSource).catch((error) => { // eslint-disable-next-line no-console - console.warning(`skipping image download and conversion due to ${error}`); + console.warn(`skipping image download and conversion due to ${error}`); }); if (base64String && mimeTypes.lookup(imageSource)) { @@ -1023,7 +1049,7 @@ const buildParagraph = async (vNode, attributes, docxDocumentInstance) => { if (isValidUrl(imageSource)) { base64String = await imageToBase64(imageSource).catch((error) => { // eslint-disable-next-line no-console - console.warning(`skipping image download and conversion due to ${error}`); + console.warn(`skipping image download and conversion due to ${error}`); }); if (base64String && mimeTypes.lookup(imageSource)) { @@ -1064,16 +1090,16 @@ const buildParagraph = async (vNode, attributes, docxDocumentInstance) => { return paragraphFragment; }; -const buildGridSpanFragment = (spanValue) => +const buildGridSpanFragment = (spanValue: number) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'gridSpan') - .att('@w', 'val', spanValue) + .att('@w', 'val', String(spanValue)) .up(); const buildTableCellSpacing = (cellSpacing = 0) => fragment({ namespaceAlias: { w: namespaces.w } }) .ele('@w', 'tblCellSpacing') - .att('@w', 'w', cellSpacing) + .att('@w', 'w', String(cellSpacing)) .att('@w', 'type', 'dxa') .up(); @@ -1154,7 +1180,7 @@ const buildTableCellProperties = (attributes) => { return tableCellPropertiesFragment; }; -const fixupTableCellBorder = (vNode, attributes) => { +const fixupTableCellBorder = (vNode: VNode, attributes) => { if (Object.prototype.hasOwnProperty.call(vNode.properties.style, 'border')) { if (vNode.properties.style.border === 'none' || vNode.properties.style.border === 0) { attributes.tableCellBorder = {}; @@ -1639,17 +1665,18 @@ const buildTableProperties = (attributes) => { return tablePropertiesFragment; }; -const cssBorderParser = (borderString) => { - let [size, stroke, color] = borderString.split(' '); +const cssBorderParser = (borderString: string) => { + let [size, stroke, color]: [string | number, 'dashed' | 'dotted' | 'double' | 'single', string] = + borderString.split(' '); if (pointRegex.test(size)) { - const matchedParts = size.match(pointRegex); - // convert point to eighth of a point - size = pointToEIP(matchedParts[1]); + const matchedParts = size.match(pointRegex) as string[]; + + size = pointToEIP(parseFloat(matchedParts[1])); } else if (pixelRegex.test(size)) { - const matchedParts = size.match(pixelRegex); - // convert pixels to eighth of a point - size = pixelToEIP(matchedParts[1]); + const matchedParts = size.match(pixelRegex) as string[]; + + size = pixelToEIP(parseFloat(matchedParts[1])); } stroke = stroke && ['dashed', 'dotted', 'double'].includes(stroke) ? stroke : 'single'; @@ -1658,14 +1685,16 @@ const cssBorderParser = (borderString) => { return [size, stroke, color]; }; -const buildTable = async (vNode, attributes, docxDocumentInstance) => { +const buildTable = async (vNode: VNode, attributes, docxDocumentInstance) => { const tableFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele('@w', 'tbl'); const modifiedAttributes = { ...attributes }; if (isVNode(vNode) && vNode.properties) { const tableAttributes = vNode.properties.attributes || {}; const tableStyles = vNode.properties.style || {}; - const tableBorders = {}; - const tableCellBorders = {}; + const tableBorders: Partial< + Record<'top' | 'bottom' | 'left' | 'right' | 'stroke' | 'color' | 'insideV' | 'insideH', any> + > = {}; + const tableCellBorders: Partial> = {}; let [borderSize, borderStrike, borderColor] = [2, 'single', '000000']; // eslint-disable-next-line no-restricted-globals @@ -1831,11 +1860,11 @@ const buildPresetGeometry = () => .att('prst', 'rect') .up(); -const buildExtents = ({ width, height }) => +const buildExtents = ({ width, height }: { width: number; height: number }) => fragment({ namespaceAlias: { a: namespaces.a } }) .ele('@a', 'ext') - .att('cx', width) - .att('cy', height) + .att('cx', String(width)) + .att('cy', String(height)) .up(); const buildOffset = () => @@ -1899,7 +1928,7 @@ const buildSrcRectFragment = () => .att('t', '0') .up(); -const buildBinaryLargeImageOrPicture = (relationshipId) => +const buildBinaryLargeImageOrPicture = (relationshipId: string) => fragment({ namespaceAlias: { a: namespaces.a, r: namespaces.r }, }) @@ -1909,7 +1938,7 @@ const buildBinaryLargeImageOrPicture = (relationshipId) => .att('cstate', 'print') .up(); -const buildBinaryLargeImageOrPictureFill = (relationshipId) => { +const buildBinaryLargeImageOrPictureFill = (relationshipId: string) => { const binaryLargeImageOrPictureFillFragment = fragment({ namespaceAlias: { pic: namespaces.pic }, }).ele('@pic', 'blipFill'); @@ -1931,8 +1960,8 @@ const buildNonVisualPictureDrawingProperties = () => .up(); const buildNonVisualDrawingProperties = ( - pictureId, - pictureNameWithExtension, + pictureId: string, + pictureNameWithExtension: string, pictureDescription = '' ) => fragment({ namespaceAlias: { pic: namespaces.pic } }) @@ -1944,8 +1973,8 @@ const buildNonVisualDrawingProperties = ( const buildNonVisualPictureProperties = ( pictureId, - pictureNameWithExtension, - pictureDescription + pictureNameWithExtension: string, + pictureDescription: string ) => { const nonVisualPicturePropertiesFragment = fragment({ namespaceAlias: { pic: namespaces.pic }, @@ -1971,6 +2000,13 @@ const buildPicture = ({ relationshipId, width, height, +}: { + id: string; + fileNameWithExtension: string; + description: string; + relationshipId: string; + width: number; + height: number; }) => { const pictureFragment = fragment({ namespaceAlias: { pic: namespaces.pic } }).ele('@pic', 'pic'); const nonVisualPicturePropertiesFragment = buildNonVisualPictureProperties( @@ -1988,7 +2024,7 @@ const buildPicture = ({ return pictureFragment; }; -const buildGraphicData = (graphicType, attributes) => { +const buildGraphicData = (graphicType: string, attributes) => { const graphicDataFragment = fragment({ namespaceAlias: { a: namespaces.a } }) .ele('@a', 'graphicData') .att('uri', 'http://schemas.openxmlformats.org/drawingml/2006/picture'); @@ -2001,7 +2037,7 @@ const buildGraphicData = (graphicType, attributes) => { return graphicDataFragment; }; -const buildGraphic = (graphicType, attributes) => { +const buildGraphic = (graphicType: string, attributes) => { const graphicFragment = fragment({ namespaceAlias: { a: namespaces.a } }).ele('@a', 'graphic'); // TODO: Handle drawing type const graphicDataFragment = buildGraphicData(graphicType, attributes); @@ -2011,7 +2047,7 @@ const buildGraphic = (graphicType, attributes) => { return graphicFragment; }; -const buildDrawingObjectNonVisualProperties = (pictureId, pictureName) => +const buildDrawingObjectNonVisualProperties = (pictureId: string, pictureName: string) => fragment({ namespaceAlias: { wp: namespaces.wp } }) .ele('@wp', 'docPr') .att('id', pictureId) @@ -2043,11 +2079,11 @@ const buildEffectExtentFragment = () => .att('t', '0') .up(); -const buildExtent = ({ width, height }) => +const buildExtent = ({ width, height }: { width: number; height: number }) => fragment({ namespaceAlias: { wp: namespaces.wp } }) .ele('@wp', 'extent') - .att('cx', width) - .att('cy', height) + .att('cx', String(width)) + .att('cy', String(height)) .up(); const buildPositionV = () => @@ -2075,7 +2111,7 @@ const buildSimplePos = () => .att('y', '0') .up(); -const buildAnchoredDrawing = (graphicType, attributes) => { +const buildAnchoredDrawing = (graphicType: string, attributes) => { const anchoredDrawingFragment = fragment({ namespaceAlias: { wp: namespaces.wp } }) .ele('@wp', 'anchor') .att('distB', '0') @@ -2114,7 +2150,7 @@ const buildAnchoredDrawing = (graphicType, attributes) => { return anchoredDrawingFragment; }; -const buildInlineDrawing = (graphicType, attributes) => { +const buildInlineDrawing = (graphicType: string, attributes) => { const inlineDrawingFragment = fragment({ namespaceAlias: { wp: namespaces.wp } }) .ele('@wp', 'inline') .att('distB', '0') @@ -2139,7 +2175,7 @@ const buildInlineDrawing = (graphicType, attributes) => { return inlineDrawingFragment; }; -const buildDrawing = (inlineOrAnchored = false, graphicType, attributes) => { +const buildDrawing = (inlineOrAnchored = false, graphicType: string, attributes) => { const drawingFragment = fragment({ namespaceAlias: { w: namespaces.w } }).ele('@w', 'drawing'); const inlineOrAnchoredDrawingFragment = inlineOrAnchored ? buildInlineDrawing(graphicType, attributes) diff --git a/src/html-to-docx.js b/src/html-to-docx.ts similarity index 84% rename from src/html-to-docx.js rename to src/html-to-docx.ts index e311e4e7..d3c007c2 100644 --- a/src/html-to-docx.js +++ b/src/html-to-docx.ts @@ -4,6 +4,7 @@ import VText from 'virtual-dom/vnode/vtext'; // eslint-disable-next-line import/no-named-default import { default as HTMLToVDOM } from 'html-to-vdom'; import { decode } from 'html-entities'; +import JSZip from 'jszip'; import { relsXML } from './schemas'; import DocxDocument from './docx-document'; @@ -33,19 +34,21 @@ import { themeFolder, themeType, } from './constants'; +import { DocumentOptions } from './interface'; const convertHTML = HTMLToVDOM({ VNode, VText, }); -const mergeOptions = (options, patch) => ({ ...options, ...patch }); +const mergeOptions = (options: Type, patch: Type): Type => ({ ...options, ...patch }); -const fixupFontSize = (fontSize) => { +const fixupFontSize = (fontSize?: string | number) => { let normalizedFontSize; - if (pointRegex.test(fontSize)) { - const matchedParts = fontSize.match(pointRegex); + if (typeof fontSize === 'string' && pointRegex.test(fontSize)) { + const matchedParts = fontSize.match(pointRegex) as RegExpMatchArray; + // @ts-ignore normalizedFontSize = pointToHIP(matchedParts[1]); } else if (fontSize) { // assuming it is already in HIP @@ -57,23 +60,33 @@ const fixupFontSize = (fontSize) => { return normalizedFontSize; }; -const normalizeUnits = (dimensioningObject, defaultDimensionsProperty) => { - let normalizedUnitResult = {}; +const normalizeUnits = ( + dimensioningObject: DocumentOptions['pageSize'] | DocumentOptions['margins'], + defaultDimensionsProperty: DocumentOptions['pageSize'] | DocumentOptions['margins'] +) => { + let normalizedUnitResult: Partial< + DocumentOptions['pageSize'] | DocumentOptions['margins'] + > | null = {}; if (typeof dimensioningObject === 'object' && dimensioningObject !== null) { Object.keys(dimensioningObject).forEach((key) => { if (pixelRegex.test(dimensioningObject[key])) { - const matchedParts = dimensioningObject[key].match(pixelRegex); + const matchedParts = dimensioningObject[key].match(pixelRegex) as RegExpMatchArray; + // @ts-ignore normalizedUnitResult[key] = pixelToTWIP(matchedParts[1]); } else if (cmRegex.test(dimensioningObject[key])) { - const matchedParts = dimensioningObject[key].match(cmRegex); + const matchedParts = dimensioningObject[key].match(cmRegex) as RegExpMatchArray; + // @ts-ignore normalizedUnitResult[key] = cmToTWIP(matchedParts[1]); } else if (inchRegex.test(dimensioningObject[key])) { - const matchedParts = dimensioningObject[key].match(inchRegex); + const matchedParts = dimensioningObject[key].match(inchRegex) as RegExpMatchArray; + // @ts-ignore normalizedUnitResult[key] = inchToTWIP(matchedParts[1]); } else if (dimensioningObject[key]) { + // @ts-ignore normalizedUnitResult[key] = dimensioningObject[key]; } else { // incase value is something like 0 + // @ts-ignore normalizedUnitResult[key] = defaultDimensionsProperty[key]; } }); @@ -85,13 +98,16 @@ const normalizeUnits = (dimensioningObject, defaultDimensionsProperty) => { return normalizedUnitResult; }; -const normalizeDocumentOptions = (documentOptions) => { +const normalizeDocumentOptions = ( + documentOptions: Partial +): Partial => { const normalizedDocumentOptions = { ...documentOptions }; Object.keys(documentOptions).forEach((key) => { // eslint-disable-next-line default-case switch (key) { case 'pageSize': case 'margins': + // @ts-ignore normalizedDocumentOptions[key] = normalizeUnits( documentOptions[key], defaultDocumentOptions[key] @@ -110,13 +126,14 @@ const normalizeDocumentOptions = (documentOptions) => { // Ref: https://en.wikipedia.org/wiki/Office_Open_XML_file_formats // http://officeopenxml.com/anatomyofOOXML.php async function addFilesToContainer( - zip, - htmlString, - suppliedDocumentOptions, - headerHTMLString, - footerHTMLString + zip: JSZip, + htmlString: string, + suppliedDocumentOptions: Partial, + headerHTMLString: string, + footerHTMLString: string ) { const normalizedDocumentOptions = normalizeDocumentOptions(suppliedDocumentOptions); + // @ts-ignore const documentOptions = mergeOptions(defaultDocumentOptions, normalizedDocumentOptions); if (documentOptions.header && !headerHTMLString) { @@ -137,6 +154,7 @@ async function addFilesToContainer( // Conversion to Word XML happens here docxDocument.documentXML = await renderDocumentFile(docxDocument); + // @ts-ignore zip .folder(relsFolderName) .file( @@ -145,6 +163,7 @@ async function addFilesToContainer( { createFolders: false } ); + // @ts-ignore zip.folder('docProps').file('core.xml', docxDocument.generateCoreXML(), { createFolders: false, }); @@ -164,6 +183,7 @@ async function addFilesToContainer( internalRelationship ); + // @ts-ignore zip.folder(wordFolder).file(fileNameWithExt, headerXML.toString({ prettyPrint: true }), { createFolders: false, }); @@ -185,6 +205,7 @@ async function addFilesToContainer( internalRelationship ); + // @ts-ignore zip.folder(wordFolder).file(fileNameWithExt, footerXML.toString({ prettyPrint: true }), { createFolders: false, }); @@ -198,6 +219,7 @@ async function addFilesToContainer( `${themeFolder}/${themeFileNameWithExt}`, internalRelationship ); + // @ts-ignore zip .folder(wordFolder) .folder(themeFolder) @@ -205,6 +227,7 @@ async function addFilesToContainer( createFolders: false, }); + // @ts-ignore zip .folder(wordFolder) .file('document.xml', docxDocument.generateDocumentXML(), { createFolders: false }) @@ -217,6 +240,7 @@ async function addFilesToContainer( const relationshipXMLs = docxDocument.generateRelsXML(); if (relationshipXMLs && Array.isArray(relationshipXMLs)) { relationshipXMLs.forEach(({ fileName, xmlString }) => { + // @ts-ignore zip.folder(wordFolder).folder(relsFolderName).file(`${fileName}.xml.rels`, xmlString, { createFolders: false, }); diff --git a/index.js b/src/index.ts similarity index 60% rename from index.js rename to src/index.ts index fe3b490d..e75d82a6 100644 --- a/index.js +++ b/src/index.ts @@ -1,10 +1,12 @@ /* eslint-disable no-useless-escape */ import JSZip from 'jszip'; -import addFilesToContainer from './src/html-to-docx'; -const minifyHTMLString = (htmlString) => { +import addFilesToContainer from './html-to-docx'; +import { DocumentOptions } from './interface'; + +const minifyHTMLString = (htmlString: string) => { try { - if (typeof htmlString === 'string' || htmlString instanceof String) { + if (typeof htmlString === 'string' || (htmlString as any) instanceof String) { const minifiedHTMLString = htmlString .replace(/\n/g, ' ') .replace(/\r/g, ' ') @@ -22,11 +24,17 @@ const minifyHTMLString = (htmlString) => { } }; +/** + * @param htmlString clean html string equivalent of document content. + * @param headerHTMLString clean html string equivalent of header. Defaults to

if header flag is true. + * @param documentOptions + * @param footerHTMLString clean html string equivalent of footer. Defaults to

if footer flag is true. + */ async function generateContainer( - htmlString, - headerHTMLString, - documentOptions = {}, - footerHTMLString + htmlString: string, + headerHTMLString: string, + documentOptions: DocumentOptions = {}, + footerHTMLString: string ) { const zip = new JSZip(); @@ -34,13 +42,13 @@ async function generateContainer( let headerHTML = headerHTMLString; let footerHTML = footerHTMLString; if (htmlString) { - contentHTML = minifyHTMLString(contentHTML); + contentHTML = minifyHTMLString(contentHTML) as string; } if (headerHTMLString) { - headerHTML = minifyHTMLString(headerHTML); + headerHTML = minifyHTMLString(headerHTML) as string; } if (footerHTMLString) { - footerHTML = minifyHTMLString(footerHTML); + footerHTML = minifyHTMLString(footerHTML) as string; } await addFilesToContainer(zip, contentHTML, documentOptions, headerHTML, footerHTML); diff --git a/src/interface.ts b/src/interface.ts new file mode 100644 index 00000000..605eb61f --- /dev/null +++ b/src/interface.ts @@ -0,0 +1,205 @@ +export interface Numbering { + /** + * defaultOrderedListStyleType default ordered list style type. Defaults to `decimal`. + */ + defaultOrderedListStyleType?: string; +} + +export interface LineNumberOptions { + /** + * start <[Number]> start of the numbering - 1. Defaults to `0`. + */ + start: number; + + /** + * countBy <[Number]> skip numbering in how many lines in between + 1. Defaults to `1`. + */ + countBy: number; + + /** + * restart <"continuous"|"newPage"|"newSection"> numbering restart strategy. Defaults to `continuous`. + */ + restart: 'continuous' | 'newPage' | 'newSection'; +} + +export interface Margins { + /** + * top distance between the top of the text margins for the main document and the top of the page for all pages in this section in TWIP. + * Defaults to 1440. Supports equivalent measurement in pixel, cm or inch. + */ + top: number; + + /** + * right distance between the right edge of the page and the right edge of the text extents for this document in TWIP. + * Defaults to 1800. Supports equivalent measurement in pixel, cm or inch. + */ + right: number; + + /** + * bottom distance between the bottom of text margins for the document and the bottom of the page in TWIP. + * Defaults to 1440. Supports equivalent measurement in pixel, cm or inch. + */ + bottom: number; + + /** + * left distance between the left edge of the page and the left edge of the text extents for this document in TWIP. + * Defaults to 1800. Supports equivalent measurement in pixel, cm or inch. + */ + left: number; + + /** + * header distance from the top edge of the page to the top edge of the header in TWIP. + * Defaults to 720. Supports equivalent measurement in pixel, cm or inch. + */ + header: number; + + /** + * footer distance from the bottom edge of the page to the bottom edge of the footer in TWIP. + * Defaults to 720. Supports equivalent measurement in pixel, cm or inch. + */ + footer: number; + + /** + * gutter amount of extra space added to the specified margin, above any existing margin values. This setting is typically used when a document is being created for binding in TWIP. + * Defaults to 0. Supports equivalent measurement in pixel, cm or inch. + */ + gutter: number; +} + +export interface Row { + /** + * cantSplit flag to allow table row to split across pages. Defaults to false. + */ + cantSplit?: boolean; +} + +export interface Table { + row?: Row; +} + +export interface PageSize { + /** + * width <[Number]> width of the page for all pages in this section in [TWIP]. Defaults to 12240. Maximum 31680. Supports equivalent measurement in [pixel], [cm] or [inch]. + */ + width: number; + + /** + * height <[Number]> height of the page for all pages in this section in [TWIP]. Defaults to 15840. Maximum 31680. Supports equivalent measurement in [pixel], [cm] or [inch]. + */ + height: number; +} + +export interface DocumentOptions { + /** + * orientation <"portrait"|"landscape"> defines the general orientation of the document. Defaults to portrait. + */ + orientation?: 'portrait' | 'landscape'; + + /** + * pageSize Defaults to U.S. letter portrait orientation. + */ + pageSize?: PageSize; + + margins?: Margins; + + /** + * title title of the document. + */ + title?: string; + + /** + * subject subject of the document. + */ + subject?: string; + + /** + * creator creator of the document. Defaults to html-to-docx. + */ + creator?: string; + + /** + * keywords > keywords associated with the document. Defaults to ['html-to-docx']. + */ + keywords?: string[]; + + /** + * description description of the document. + */ + description?: string; + + /** + * lastModifiedBy last modifier of the document. Defaults to html-to-docx. + */ + lastModifiedBy?: string; + + /** + * revision revision of the document. Defaults to 1. + */ + revision?: number; + + /** + * createdAt time of creation of the document. Defaults to current time. + */ + createdAt?: Date; + + /** + * modifiedAt time of last modification of the document. Defaults to current time. + */ + modifiedAt?: Date; + + /** + * headerType <"default"|"first"|"even"> type of header. Defaults to default. + */ + headerType?: 'default' | 'first' | 'even'; + + /** + * header flag to enable header. Defaults to false. + */ + header?: boolean; + + /** + * footerType <"default"|"first"|"even"> type of footer. Defaults to default. + */ + footerType?: 'default' | 'first' | 'even'; + + /** + * footer flag to enable footer. Defaults to false. + */ + footer?: boolean; + + /** + * font font name to be used. Defaults to Times New Roman. + */ + font?: string; + + /** + * fontSize size of font in HIP(Half of point). Defaults to 22. Supports equivalent measure in pt. + */ + fontSize?: number; + + /** + * complexScriptFontSize size of complex script font in HIP(Half of point). Defaults to 22. Supports equivalent measure in pt. + */ + complexScriptFontSize?: number; + + table?: Table; + + /** + * pageNumber flag to enable page number in footer. Defaults to false. Page number works only if footer flag is set as true. + */ + pageNumber?: boolean; + + /** + * skipFirstHeaderFooter flag to skip first page header and footer. Defaults to false. + */ + skipFirstHeaderFooter?: boolean; + + /** + * lineNumber flag to enable line numbering. Defaults to `false`. + */ + lineNumber?: boolean; + + lineNumberOptions?: LineNumberOptions; + + numbering?: Numbering; +} diff --git a/src/namespaces.js b/src/namespaces.ts similarity index 100% rename from src/namespaces.js rename to src/namespaces.ts diff --git a/src/schemas/content-types.js b/src/schemas/content-types.ts similarity index 100% rename from src/schemas/content-types.js rename to src/schemas/content-types.ts diff --git a/src/schemas/core.js b/src/schemas/core.ts similarity index 100% rename from src/schemas/core.js rename to src/schemas/core.ts diff --git a/src/schemas/document-rels.js b/src/schemas/document-rels.ts similarity index 100% rename from src/schemas/document-rels.js rename to src/schemas/document-rels.ts diff --git a/src/schemas/document.template.js b/src/schemas/document.template.ts similarity index 100% rename from src/schemas/document.template.js rename to src/schemas/document.template.ts diff --git a/src/schemas/font-table.js b/src/schemas/font-table.ts similarity index 100% rename from src/schemas/font-table.js rename to src/schemas/font-table.ts diff --git a/src/schemas/generic-rels.js b/src/schemas/generic-rels.ts similarity index 100% rename from src/schemas/generic-rels.js rename to src/schemas/generic-rels.ts diff --git a/src/schemas/index.js b/src/schemas/index.ts similarity index 100% rename from src/schemas/index.js rename to src/schemas/index.ts diff --git a/src/schemas/numbering.js b/src/schemas/numbering.ts similarity index 100% rename from src/schemas/numbering.js rename to src/schemas/numbering.ts diff --git a/src/schemas/rels.js b/src/schemas/rels.ts similarity index 100% rename from src/schemas/rels.js rename to src/schemas/rels.ts diff --git a/src/schemas/settings.js b/src/schemas/settings.ts similarity index 100% rename from src/schemas/settings.js rename to src/schemas/settings.ts diff --git a/src/schemas/styles.js b/src/schemas/styles.ts similarity index 100% rename from src/schemas/styles.js rename to src/schemas/styles.ts diff --git a/src/schemas/theme.js b/src/schemas/theme.ts similarity index 100% rename from src/schemas/theme.js rename to src/schemas/theme.ts diff --git a/src/schemas/web-settings.js b/src/schemas/web-settings.ts similarity index 100% rename from src/schemas/web-settings.js rename to src/schemas/web-settings.ts diff --git a/src/utils/color-conversion.js b/src/utils/color-conversion.ts similarity index 86% rename from src/utils/color-conversion.js rename to src/utils/color-conversion.ts index e878e944..48d99c8e 100644 --- a/src/utils/color-conversion.js +++ b/src/utils/color-conversion.ts @@ -5,12 +5,12 @@ export const hslRegex = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/i; export const hexRegex = /#([0-9A-F]{6})/i; export const hex3Regex = /#([0-9A-F])([0-9A-F])([0-9A-F])/i; -// eslint-disable-next-line import/prefer-default-export -export const rgbToHex = (red, green, blue) => { +export type RGBComponent = string | number + +export const rgbToHex = (red: RGBComponent, green: RGBComponent, blue: RGBComponent) => { const hexColorCode = [red, green, blue] .map((x) => { - // eslint-disable-next-line radix, no-param-reassign - x = parseInt(x).toString(16); + x = parseInt(x).toString(16); return x.length === 1 ? `0${x}` : x; }) .join(''); @@ -53,7 +53,7 @@ export const hslToHex = (hue, saturation, luminosity) => { .join(''); }; -export const hex3ToHex = (red, green, blue) => { +export const hex3ToHex = (red: string, green: string, blue: string) => { const hexColorCode = [red, green, blue].map((x) => `${x}${x}`).join(''); return hexColorCode; diff --git a/src/utils/list.js b/src/utils/list.ts similarity index 84% rename from src/utils/list.js rename to src/utils/list.ts index 3c190d96..9ca9316b 100644 --- a/src/utils/list.js +++ b/src/utils/list.ts @@ -1,12 +1,12 @@ class ListStyleBuilder { - // defaults is an object passed in from constants.js / numbering with the following properties: - // defaultOrderedListStyleType: 'decimal' (unless otherwise specified) + private defaults; + constructor(defaults) { this.defaults = defaults || { defaultOrderedListStyleType: 'decimal' }; } // eslint-disable-next-line class-methods-use-this - getListStyleType(listType) { + getListStyleType(listType?: string) { switch (listType) { case 'upper-roman': return 'upperRoman'; @@ -26,7 +26,7 @@ class ListStyleBuilder { } } - getListPrefixSuffix(style, lvl) { + getListPrefixSuffix(style, lvl: number) { let listType = this.defaults.defaultOrderedListStyleType; if (style && style['list-style-type']) { diff --git a/src/utils/unit-conversion.js b/src/utils/unit-conversion.js deleted file mode 100644 index 35ab9c18..00000000 --- a/src/utils/unit-conversion.js +++ /dev/null @@ -1,53 +0,0 @@ -export const pixelRegex = /([\d.]+)px/i; -export const percentageRegex = /([\d.]+)%/i; -export const pointRegex = /([\d.]+)pt/i; -export const cmRegex = /([\d.]+)cm/i; -export const inchRegex = /([\d.]+)in/i; - -export const pixelToEMU = (pixelValue) => Math.round(pixelValue * 9525); - -export const EMUToPixel = (EMUValue) => Math.round(EMUValue / 9525); - -export const TWIPToEMU = (TWIPValue) => Math.round(TWIPValue * 635); - -export const EMUToTWIP = (EMUValue) => Math.round(EMUValue / 635); - -export const pointToTWIP = (pointValue) => Math.round(pointValue * 20); - -export const TWIPToPoint = (TWIPValue) => Math.round(TWIPValue / 20); - -export const pointToHIP = (pointValue) => Math.round(pointValue * 2); - -export const HIPToPoint = (HIPValue) => Math.round(HIPValue / 2); - -export const HIPToTWIP = (HIPValue) => Math.round(HIPValue * 10); - -export const TWIPToHIP = (TWIPValue) => Math.round(TWIPValue / 10); - -export const pixelToTWIP = (pixelValue) => EMUToTWIP(pixelToEMU(pixelValue)); - -export const TWIPToPixel = (TWIPValue) => EMUToPixel(TWIPToEMU(TWIPValue)); - -export const pixelToHIP = (pixelValue) => TWIPToHIP(EMUToTWIP(pixelToEMU(pixelValue))); - -export const HIPToPixel = (HIPValue) => EMUToPixel(TWIPToEMU(HIPToTWIP(HIPValue))); - -export const inchToPoint = (inchValue) => Math.round(inchValue * 72); - -export const inchToTWIP = (inchValue) => pointToTWIP(inchToPoint(inchValue)); - -export const cmToInch = (cmValue) => cmValue * 0.3937008; - -export const cmToTWIP = (cmValue) => inchToTWIP(cmToInch(cmValue)); - -export const pixelToPoint = (pixelValue) => HIPToPoint(pixelToHIP(pixelValue)); - -export const pointToPixel = (pointValue) => HIPToPixel(pointToHIP(pointValue)); - -export const EIPToPoint = (EIPValue) => Math.round(EIPValue / 8); - -export const pointToEIP = (PointValue) => Math.round(PointValue * 8); - -export const pixelToEIP = (pixelValue) => pointToEIP(pixelToPoint(pixelValue)); - -export const EIPToPixel = (EIPValue) => pointToPixel(EIPToPoint(EIPValue)); diff --git a/src/utils/unit-conversion.ts b/src/utils/unit-conversion.ts new file mode 100644 index 00000000..d0870d16 --- /dev/null +++ b/src/utils/unit-conversion.ts @@ -0,0 +1,53 @@ +export const pixelRegex = /([\d.]+)px/i; +export const percentageRegex = /([\d.]+)%/i; +export const pointRegex = /([\d.]+)pt/i; +export const cmRegex = /([\d.]+)cm/i; +export const inchRegex = /([\d.]+)in/i; + +export const pixelToEMU = (pixelValue: number) => Math.round(pixelValue * 9525); + +export const EMUToPixel = (EMUValue: number) => Math.round(EMUValue / 9525); + +export const TWIPToEMU = (TWIPValue: number) => Math.round(TWIPValue * 635); + +export const EMUToTWIP = (EMUValue: number) => Math.round(EMUValue / 635); + +export const pointToTWIP = (pointValue: number) => Math.round(pointValue * 20); + +export const TWIPToPoint = (TWIPValue: number) => Math.round(TWIPValue / 20); + +export const pointToHIP = (pointValue: number) => Math.round(pointValue * 2); + +export const HIPToPoint = (HIPValue: number) => Math.round(HIPValue / 2); + +export const HIPToTWIP = (HIPValue: number) => Math.round(HIPValue * 10); + +export const TWIPToHIP = (TWIPValue: number) => Math.round(TWIPValue / 10); + +export const pixelToTWIP = (pixelValue: number) => EMUToTWIP(pixelToEMU(pixelValue)); + +export const TWIPToPixel = (TWIPValue: number) => EMUToPixel(TWIPToEMU(TWIPValue)); + +export const pixelToHIP = (pixelValue: number) => TWIPToHIP(EMUToTWIP(pixelToEMU(pixelValue))); + +export const HIPToPixel = (HIPValue: number) => EMUToPixel(TWIPToEMU(HIPToTWIP(HIPValue))); + +export const inchToPoint = (inchValue: number) => Math.round(inchValue * 72); + +export const inchToTWIP = (inchValue: number) => pointToTWIP(inchToPoint(inchValue)); + +export const cmToInch = (cmValue: number) => cmValue * 0.3937008; + +export const cmToTWIP = (cmValue: number) => inchToTWIP(cmToInch(cmValue)); + +export const pixelToPoint = (pixelValue: number) => HIPToPoint(pixelToHIP(pixelValue)); + +export const pointToPixel = (pointValue: number) => HIPToPixel(pointToHIP(pointValue)); + +export const EIPToPoint = (EIPValue: number) => Math.round(EIPValue / 8); + +export const pointToEIP = (PointValue: number) => Math.round(PointValue * 8); + +export const pixelToEIP = (pixelValue: number) => pointToEIP(pixelToPoint(pixelValue)); + +export const EIPToPixel = (EIPValue: number) => pointToPixel(EIPToPoint(EIPValue)); diff --git a/src/utils/url.js b/src/utils/url.ts similarity index 83% rename from src/utils/url.js rename to src/utils/url.ts index 4ce8db4d..0dbd4201 100644 --- a/src/utils/url.js +++ b/src/utils/url.ts @@ -1,4 +1,4 @@ -const isValidUrl = (urlString) => { +const isValidUrl = (urlString: string) => { const urlRegex = /http(s)?:\/\/(\w+:?\w*@)?(\S+)(:\d+)?((?<=\.)\w+)+(\/([\w#!:.?+=&%@!\-/])*)?/gi; return Boolean(urlRegex.test(urlString)); diff --git a/src/utils/vnode.js b/src/utils/vnode.ts similarity index 61% rename from src/utils/vnode.js rename to src/utils/vnode.ts index 0b620d50..a16eb8d2 100644 --- a/src/utils/vnode.js +++ b/src/utils/vnode.ts @@ -1,3 +1,5 @@ +import { VNode } from "virtual-dom"; + // eslint-disable-next-line import/prefer-default-export -export const vNodeHasChildren = (vNode) => +export const vNodeHasChildren = (vNode: VNode) => vNode && vNode.children && Array.isArray(vNode.children) && vNode.children.length; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..8bc16bf0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,105 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./build", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + "noImplicitThis": false, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist/**/**"] +}