Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@ _ci_*

.pnpm-store

# Test artifacts
packages/*/test/webpack5-angular-project
# Test artifacts - only ignore generated files, not source
packages/*/test/webpack5-angular-project/node_modules
packages/*/test/webpack5-angular-project/dist
packages/*/test/webpack5-angular-project/.angular
packages/*/test/webpack5-angular-project/package-lock.json
packages/*/test/webpack5-build-test
.playwright-mcp/
8 changes: 8 additions & 0 deletions packages/plugin-print-ready-pdfs-web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ pnpm-debug.log*
test/converted-pdfx3.pdf
test-results/
.vercel

# Webpack 5 test projects (generated)
test/webpack5-build-test/
test/webpack5-angular-project/node_modules/
test/webpack5-angular-project/dist/
test/webpack5-angular-project/.angular/
test/webpack5-angular-cesdk-project/
test/cp-command-test/
20 changes: 20 additions & 0 deletions packages/plugin-print-ready-pdfs-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.2] - 2025-12-23

### Fixed

- Fixed Webpack 5 **runtime** compatibility issue where `import.meta.url` was transformed to `file://` URLs at build time, causing "Cannot find module" errors in Angular 17+ and other Webpack 5 bundled environments

### Added

- New `AssetLoader` interface for customizing how plugin assets (gs.js, gs.wasm, ICC profiles) are loaded
- `BrowserAssetLoader` for browser environments with configurable `assetPath` option
- `NodeAssetLoader` for Node.js environments (used automatically)
- New `assetPath` option for `convertToPDFX3()` to specify where assets are served from
- Separate browser/node entry points via conditional exports in package.json
- New Playwright test suite for Angular + Webpack 5 runtime verification (`pnpm test:webpack5:angular`)

### Changed

- Refactored asset loading to use dependency injection pattern for better bundler compatibility
- Vite and native ESM continue to work without configuration (auto-detects via `import.meta.url`)

## [1.1.1] - 2025-12-18

### Fixed
Expand Down
104 changes: 104 additions & 0 deletions packages/plugin-print-ready-pdfs-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,96 @@ This plugin automatically converts CE.SDK's RGB PDFs into print-ready files that
npm install @imgly/plugin-print-ready-pdfs-web
```

## Bundler Setup (Webpack 5 / Angular)

When using bundlers like Webpack 5 or Angular CLI, the plugin's WASM assets need to be copied to your public folder and the `assetPath` option must be provided.

**Why is this needed?** Webpack 5 transforms `import.meta.url` to `file://` URLs at build time, which don't work in browsers. The `assetPath` option tells the plugin where to find its assets at runtime.

### Angular CLI

Add to your `angular.json` in the `assets` array:

```json
{
"glob": "{gs.js,gs.wasm,*.icc}",
"input": "node_modules/@imgly/plugin-print-ready-pdfs-web/dist",
"output": "/assets/print-ready-pdfs"
}
```

Then provide the `assetPath` option:

```javascript
const printReadyPDF = await convertToPDFX3(pdfBlob, {
outputProfile: 'fogra39',
assetPath: '/assets/print-ready-pdfs/'
});
```

### Webpack 5

Use `copy-webpack-plugin` to copy the assets:

```javascript
const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
plugins: [
new CopyPlugin({
patterns: [{
from: 'node_modules/@imgly/plugin-print-ready-pdfs-web/dist/*.{js,wasm,icc}',
to: 'assets/[name][ext]'
}]
})
]
};
```

Then provide the `assetPath` option:

```javascript
const printReadyPDF = await convertToPDFX3(pdfBlob, {
outputProfile: 'fogra39',
assetPath: '/assets/'
});
```

### Vite / Native ESM

No additional configuration needed. The plugin automatically resolves assets using `import.meta.url`.

### Custom Asset Loading

For advanced use cases (CDN hosting, custom loading logic), implement the `AssetLoader` interface:

```typescript
import { convertToPDFX3, type AssetLoader } from '@imgly/plugin-print-ready-pdfs-web';

class CDNAssetLoader implements AssetLoader {
private cdnBase = 'https://cdn.example.com/pdf-plugin/v1.1.2/';

async loadGhostscriptModule() {
const module = await import(/* webpackIgnore: true */ this.cdnBase + 'gs.js');
return module.default;
}

getWasmPath() {
return this.cdnBase + 'gs.wasm';
}

async loadICCProfile(name: string) {
const response = await fetch(this.cdnBase + name);
return response.blob();
}
}

const printReadyPDF = await convertToPDFX3(pdfBlob, {
outputProfile: 'fogra39',
assetLoader: new CDNAssetLoader()
});
```

## Quick Start

Just one function call transforms any PDF into a print-ready file:
Expand Down Expand Up @@ -143,9 +233,23 @@ interface PDFX3Options {
outputConditionIdentifier?: string; // ICC profile identifier for OutputIntent
outputCondition?: string; // Human-readable output condition description
flattenTransparency?: boolean; // Flatten transparency (default: true for PDF/X-3)

// Asset loading (for bundler compatibility)
assetPath?: string; // URL path where plugin assets are served
assetLoader?: AssetLoader; // Custom asset loader (advanced)
}
```

**Asset Loading Options:**

| Option | When to Use |
|--------|-------------|
| Neither | Vite, native ESM - auto-detected via `import.meta.url` |
| `assetPath` | Webpack 5, Angular - specify where assets are served |
| `assetLoader` | CDN hosting, custom loading logic - full control |

See [Bundler Setup](#bundler-setup-webpack-5--angular) for configuration examples.

**Note:** Batch processing handles each PDF sequentially to avoid overwhelming the WASM module.

**OutputIntent Metadata:**
Expand Down
89 changes: 10 additions & 79 deletions packages/plugin-print-ready-pdfs-web/esbuild/config.mjs
Original file line number Diff line number Diff line change
@@ -1,102 +1,33 @@
import chalk from 'chalk';
import { readFile, copyFile, mkdir, writeFile } from 'fs/promises';
import { existsSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

import baseConfig from '../../../esbuild/config.mjs';
import log from '../../../esbuild/log.mjs';

/**
* Add webpackIgnore comments to Node.js module imports in gs.js
* This prevents Webpack 5 from trying to resolve these modules in browser builds.
* See: https://github.com/imgly/ubq/issues/11471
* esbuild configuration for plugin-print-ready-pdfs-web
*
* NOTE: This config is used by the shared build infrastructure.
* For multi-bundle builds (browser, node, universal), see scripts/build.mjs
* which handles the full build process directly.
*/
function addWebpackIgnoreComments(content) {
// Transform: await import("module") -> await import(/* webpackIgnore: true */ "module")
// Transform: await import("path") -> await import(/* webpackIgnore: true */ "path")
// Also handle other Node.js modules that might be imported
const nodeModules = ['module', 'path', 'fs', 'url', 'os'];
let transformed = content;

for (const mod of nodeModules) {
// Match: import("module") or import('module') with optional whitespace
const pattern = new RegExp(`import\\(\\s*["']${mod}["']\\s*\\)`, 'g');
transformed = transformed.replace(pattern, `import(/* webpackIgnore: true */ "${mod}")`);
}
import { readFile } from 'fs/promises';

return transformed;
}

const __dirname = dirname(fileURLToPath(import.meta.url));
import baseConfig from '../../../esbuild/config.mjs';
import log from '../../../esbuild/log.mjs';

// Avoid the Experimental Feature warning when using the above.
const packageJson = JSON.parse(
await readFile(new URL('../package.json', import.meta.url))
);

// Plugin to copy WASM, JS, and ICC profile files to dist
const copyWasmPlugin = {
name: 'copy-wasm',
setup(build) {
build.onEnd(async () => {
const distDir = join(__dirname, '../dist');

if (!existsSync(distDir)) {
await mkdir(distDir, { recursive: true });
}

// Copy WASM file
const srcWasm = join(__dirname, '../src/wasm/gs.wasm');
const distWasm = join(distDir, 'gs.wasm');
await copyFile(srcWasm, distWasm);
log(chalk.green('✓ Copied gs.wasm to dist/'));

// Copy and transform gs.js file to add webpackIgnore comments
// This fixes Webpack 5 compatibility (see https://github.com/imgly/ubq/issues/11471)
const srcJs = join(__dirname, '../src/wasm/gs.js');
const distJs = join(distDir, 'gs.js');
const gsContent = await readFile(srcJs, 'utf-8');
const transformedContent = addWebpackIgnoreComments(gsContent);
await writeFile(distJs, transformedContent);
log(chalk.green('✓ Copied and transformed gs.js to dist/ (added webpackIgnore comments)'));

// Copy ICC profile files
const iccProfiles = [
'GRACoL2013_CRPC6.icc',
'ISOcoated_v2_eci.icc',
'sRGB_IEC61966-2-1.icc'
];

for (const profile of iccProfiles) {
const srcProfile = join(__dirname, '../src/assets/icc-profiles', profile);
const distProfile = join(distDir, profile);
await copyFile(srcProfile, distProfile);
log(chalk.green(`✓ Copied ${profile} to dist/`));
}
});
}
};

export default ({ isDevelopment }) => {
log(
`${chalk.yellow('Building version:')} ${chalk.bold(packageJson.version)}`
);
log(`Building version: ${packageJson.version}`);

const config = baseConfig({
isDevelopment,
external: ['@cesdk/cesdk-js', 'path', 'url', 'fs', 'os', 'module'],
pluginVersion: packageJson.version
});

// Add loader configuration for WASM files
config.loader = {
...config.loader,
'.wasm': 'file'
};

// Add our custom plugin
config.plugins = [...(config.plugins || []), copyWasmPlugin];

return config;
};
}
30 changes: 26 additions & 4 deletions packages/plugin-print-ready-pdfs-web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@imgly/plugin-print-ready-pdfs-web",
"version": "1.1.1",
"version": "1.1.2",
"description": "Print-Ready PDFs plugin for CE.SDK editor - PDF/X conversion and export functionality. Contains AGPL-3.0 licensed Ghostscript WASM.",
"keywords": [
"CE.SDK",
Expand All @@ -27,8 +27,26 @@
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
"node": {
"import": "./dist/index.node.mjs",
"types": "./dist/index.node.d.ts"
},
"browser": {
"import": "./dist/index.browser.mjs",
"types": "./dist/index.browser.d.ts"
},
"default": {
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"./browser": {
"import": "./dist/index.browser.mjs",
"types": "./dist/index.browser.d.ts"
},
"./node": {
"import": "./dist/index.node.mjs",
"types": "./dist/index.node.d.ts"
}
},
"homepage": "https://img.ly/products/creative-sdk",
Expand Down Expand Up @@ -68,7 +86,11 @@
"test:silent": "pnpm run build && vitest run silent-conversion",
"test:all": "pnpm run test:browser && pnpm run test:integration",
"test:visual": "pnpm run build && node test/visual-test.mjs",
"test:webpack5": "bash test/webpack5-compatibility-test.sh"
"test:webpack5": "bash test/webpack5-compatibility-test.sh",
"test:webpack5:angular": "playwright test test/webpack5-angular-runtime.spec.ts --timeout 180000",
"test:webpack5:angular:setup": "cd test/webpack5-angular-project && npm install",
"test:webpack5:angular:cesdk": "playwright test test/webpack5-angular-cesdk.spec.ts --timeout 300000",
"test:webpack5:angular:cesdk:create": "bash scripts/create-angular-cesdk-test-project.sh"
},
"devDependencies": {
"@cesdk/cesdk-js": "~1.61.0",
Expand Down
Loading
Loading