diff --git a/README.md b/README.md index aef45daf..5642c449 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,148 @@ This will generate two DOCX files in the `example/typescript` directory: - `basic-example.docx` - A simple document with minimal configuration - `advanced-example.docx` - A document with headers, footers, and advanced formatting options +## Browser Standalone Build + +The library provides a standalone browser build that bundles all dependencies into a single file. This allows you to use the library directly in HTML pages without any build tools or module bundlers. + +### Build Outputs + +When you run `npm run build`, three distribution files are generated: + +| File | Format | Size | Dependencies | Use Case | +|------|--------|------|--------------|----------| +| `dist/html-to-docx.esm.js` | ES Module | ~1.6 MB | External | Modern bundlers (Webpack, Vite, Rollup) | +| `dist/html-to-docx.umd.js` | UMD | ~1.6 MB | External | Node.js, AMD, or manual dependency management | +| `dist/html-to-docx.browser.js` | IIFE | ~2.4 MB | **All bundled** | Direct browser usage, CDN, quick prototypes | + +### Build Commands + +```bash +# Build all versions (ESM + UMD + Browser) +npm run build + +# Build only the browser standalone version (development) +npm run build:browser + +# Build only the browser standalone version (production, minified) +npm run build:browser:prod +``` + +### Browser Usage + +Include the standalone browser build directly in your HTML: + +```html + + + + HTML to DOCX Demo + + + + + + + + + + + + + +``` + +### Testing the Browser Build + +A test page is included to verify the browser build works correctly: + +```bash +# Build and start the test server +npm run test:browser +``` + +Then open http://localhost:8080/tests/test_browser.html in your browser. + +The test page allows you to: +- Edit HTML content in a textarea +- Click "Generate DOCX" to create and download a Word document +- See status messages for success or errors + +### CDN Usage + +You can also host the browser build on a CDN for easy inclusion: + +```html + + +``` + +### Limitations in Browser Environment + +- **Sharp (SVG conversion)**: The `sharp` image processing library is not available in browsers. SVG images will be embedded natively (requires Office 2019+). +- **File System**: No direct file system access. Documents are returned as Blob/ArrayBuffer for download. +- **Image URLs**: Remote images must be CORS-enabled or converted to base64 data URLs. + ## Usage ```js @@ -487,10 +629,6 @@ Made with [contrib.rocks](https://contrib.rocks). --- -**Note:** Currently optimized for Node.js environments. Browser support is planned for future releases. - ---- -
[![TurboDocx](./footer.png)](https://www.turbodocx.com) diff --git a/package-lock.json b/package-lock.json index fab39dac..98cac652 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "rollup": "^2.62.0", "rollup-plugin-cleaner": "^1.0.0", "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-polyfill-node": "^0.13.0", "rollup-plugin-terser": "^7.0.2", "standard-version": "^9.3.1", "ts-node": "^10.9.2", @@ -3999,6 +4000,79 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-inject/node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-inject/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-inject/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, + "license": "MIT" + }, + "node_modules/@rollup/plugin-inject/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/plugin-json": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", @@ -13360,6 +13434,19 @@ "process-es6": "^0.11.2" } }, + "node_modules/rollup-plugin-polyfill-node": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.13.0.tgz", + "integrity": "sha512-FYEvpCaD5jGtyBuBFcQImEGmTxDTPbiHjJdrYIp+mFIwgXiXabxvKUK7ZT9P31ozu2Tqm9llYQMRWsfvTMTAOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-inject": "^5.0.4" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, "node_modules/rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", diff --git a/package.json b/package.json index b88442a0..a7b18008 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ ], "main": "dist/html-to-docx.umd.js", "module": "dist/html-to-docx.esm.js", + "browser": "dist/html-to-docx.browser.js", "types": "index.d.ts", "files": [ "dist", @@ -65,6 +66,7 @@ "test:unit": "jest", "test:unit:watch": "jest --watch", "test:unit:coverage": "jest --coverage", + "test:browser": "npm run build && npx -y serve . -l 8080 --no-clipboard", "prerelease": "npm run build:prod", "release": "standard-version", "lint": "eslint --fix .", @@ -73,6 +75,8 @@ "build": "rollup -c", "build:dev": "NODE_ENV=development rollup -c", "build:prod": "NODE_ENV=production rollup -c", + "build:browser": "cross-env BUILD_TARGET=browser rollup -c", + "build:browser:prod": "cross-env NODE_ENV=production BUILD_TARGET=browser rollup -c", "diff:docx": "node scripts/diff-docx.js", "prepare": "husky install", "postinstall": "node scripts/postinstall.js", @@ -140,6 +144,7 @@ "rollup": "^2.62.0", "rollup-plugin-cleaner": "^1.0.0", "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-polyfill-node": "^0.13.0", "rollup-plugin-terser": "^7.0.2", "standard-version": "^9.3.1", "ts-node": "^10.9.2", diff --git a/rollup.config.js b/rollup.config.js index c6d0ef6e..20d7b7d1 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,12 +4,17 @@ 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 nodePolyfills from 'rollup-plugin-polyfill-node'; import * as meta from './package.json'; const isProduction = process.env.NODE_ENV === 'production'; +const browserOnly = process.env.BUILD_TARGET === 'browser'; -export default { +const banner = `// ${meta.homepage} v${meta.version} Copyright ${new Date().getFullYear()} ${meta.author}`; + +// Node.js / Library build configuration (ESM and UMD) +const libraryConfig = { input: 'index.js', external: ['color-name', 'jszip', 'xmlbuilder2', 'html-entities', 'lru-cache', 'htmlparser2', 'sharp'], plugins: [ @@ -29,9 +34,7 @@ export default { file: 'dist/html-to-docx.esm.js', format: 'es', sourcemap: !isProduction, - banner: `// ${meta.homepage} v${meta.version} Copyright ${new Date().getFullYear()} ${ - meta.author - }`, + banner, }, { file: 'dist/html-to-docx.umd.js', @@ -44,9 +47,43 @@ export default { xmlbuilder2: 'xmlbuilder2', 'html-entities': 'htmlEntities', }, - banner: `// ${meta.homepage} v${meta.version} Copyright ${new Date().getFullYear()} ${ - meta.author - }`, + banner, }, ], }; + +// Standalone browser build configuration (all dependencies bundled) +const browserConfig = { + input: 'index.js', + // Only exclude sharp (Node.js native module, not supported in browser) + external: ['sharp'], + plugins: [ + cleaner({ + targets: ['./dist/'], + }), + resolve({ + browser: true, + preferBuiltins: false, + }), + json(), + commonjs(), + nodePolyfills(), + terser({ + mangle: isProduction, + compress: isProduction, + }), + ], + output: { + file: 'dist/html-to-docx.browser.js', + format: 'iife', + name: 'HTMLToDOCX', + sourcemap: !isProduction, + banner, + // Provide empty implementation for sharp in browser environment + globals: { + sharp: '(() => null)', + }, + }, +}; + +export default browserOnly ? [browserConfig] : [libraryConfig, browserConfig]; diff --git a/tests/test_browser.html b/tests/test_browser.html new file mode 100644 index 00000000..1f5953cb --- /dev/null +++ b/tests/test_browser.html @@ -0,0 +1,278 @@ + + + + + + HTML to DOCX Browser Test + + + +
+

🔄 HTML to DOCX Browser Test

+

Enter HTML content below and click the button to generate a DOCX file.

+ + + + + +
+
+ + + + + + +