180 lines
7.3 KiB
Markdown
180 lines
7.3 KiB
Markdown
# XSLT tools
|
|
|
|
Browser-based MVP for testing XML/XSLT transformations. The application itself
|
|
does not intentionally upload opened files, and it uses LocalStorage plus local
|
|
file dialogs where available. XSLT is executable code, however: a trusted
|
|
stylesheet may access browser or network resources, so processing is not
|
|
described as guaranteed local-only.
|
|
|
|
The application is independently deployable and also implements Toolbox Contract v1. When launched with a same-origin `?toolbox=<catalog URL>` context, the shared application shell adds toolbox navigation and an application switcher; without that context, the same build runs standalone.
|
|
|
|
## Features in this MVP
|
|
|
|
- Three-column workbench:
|
|
- XML input
|
|
- XSL transformation code
|
|
- XML output
|
|
- CodeMirror editors with:
|
|
- line numbers
|
|
- XML syntax highlighting
|
|
- editor undo/redo
|
|
- cut/copy/paste helpers
|
|
- find support via CodeMirror shortcuts
|
|
- Transformation engines:
|
|
- SaxonJS 2.7 as the default engine for XSLT 1.0/2.0/3.0-style workflows
|
|
- Browser-native `XSLTProcessor` as a lightweight XSLT 1.0 fallback
|
|
- A mandatory SHA-256 trust confirmation for the exact executable stylesheet;
|
|
every edit, replacement, format, or reset revokes trust for both engines
|
|
- Defense-in-depth input/output limits and a best-effort asynchronous deadline
|
|
- XML and XSLT well-formedness checks
|
|
- Basic XSLT root/version checks
|
|
- Snippet toolbox for common XSLT constructs
|
|
- Open/save local files
|
|
- File System Access API when supported
|
|
- file input/download fallback otherwise
|
|
- Output → Input action for chained transformations
|
|
- Confirmation dialog before destructive overwrites
|
|
- LocalStorage persistence
|
|
- Approximate “explain transformation” table after a run
|
|
- Shared toolbox shell with standalone fallback, application switching, and the XSLT-specific Help action
|
|
- Relocatable static output for deployment at root or nested paths
|
|
|
|
## SaxonJS note
|
|
|
|
SaxonJS 2.7 is vendored as the unmodified browser distribution file at `public/vendor/saxon/SaxonJS2.js` and loaded lazily when the SaxonJS engine runs. It is not bundled from the npm package during the application build.
|
|
|
|
SaxonJS is available free of charge but is not open source. Its separate redistribution terms are preserved at `public/vendor/saxon/LICENSE.txt`. Release archives include that notice as `LICENSES/SaxonJS-LICENSE.txt` alongside `LICENSES/xslt-tools-LICENSE.txt` for the AGPL-3.0-only application; do not describe the complete archive as AGPL-only.
|
|
|
|
## Executable stylesheet security
|
|
|
|
Treat every stylesheet like executable code. Transformation is disabled until
|
|
the user reviews a warning and trusts the SHA-256 fingerprint of the exact
|
|
current stylesheet. Trust is session-only and tied to both the content hash and
|
|
an edit revision; changing and then reverting text still requires another
|
|
confirmation. The same gate covers SaxonJS and native `XSLTProcessor`.
|
|
|
|
The application enforces conservative input/output limits and a best-effort
|
|
asynchronous deadline, but transformations are not isolated from the browser or
|
|
network and synchronous CPU exhaustion cannot be forcibly stopped. A trusted
|
|
SaxonJS stylesheet may read data stored by other Toolbox apps on the same site,
|
|
including PDF workspaces and portal preferences, then send it over the network
|
|
or navigate the page away.
|
|
|
|
When served with the recommended portal CSP, cross-origin fetches and dynamic
|
|
JavaScript evaluation are restricted without granting `unsafe-eval`. CSP is
|
|
defense in depth, not complete containment. Only execute stylesheets from trusted
|
|
sources.
|
|
|
|
## Run locally
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
Then open the local Vite URL shown in your terminal.
|
|
|
|
## Build
|
|
|
|
```bash
|
|
npm run build
|
|
npm run preview
|
|
```
|
|
|
|
Vite uses `base: './'`, so a single build can be hosted at an unknown nested path such as `/apps/xslt/`. The SaxonJS runtime is resolved from that application base as well.
|
|
|
|
## Toolbox manifest
|
|
|
|
`src/toolboxApp.ts` is the typed source for both the shared shell and the published `public/toolbox-app.json` manifest. Keep the generated file current with:
|
|
|
|
```bash
|
|
npm run manifest:generate
|
|
npm run manifest:check
|
|
```
|
|
|
|
Toolbox mode is contextual rather than a separate build:
|
|
|
|
```text
|
|
/apps/xslt/ standalone
|
|
/apps/xslt/?toolbox=/toolbox.catalog.json toolbox mode
|
|
```
|
|
|
|
An unavailable or invalid catalogue falls back to standalone operation.
|
|
|
|
## Release archive
|
|
|
|
After a successful build, create the deterministic application ZIP and SHA-256 sidecar with:
|
|
|
|
```bash
|
|
npm run release
|
|
```
|
|
|
|
The command refuses to overwrite a same-version archive. Use
|
|
`npm run release -- --force` only when intentionally replacing it.
|
|
|
|
The archive contains the static app and `toolbox-app.json`, plus `CHANGELOG.md`,
|
|
`SOURCE.md` with the corresponding tagged source link,
|
|
`THIRD_PARTY_NOTICES.md`, the application licence at
|
|
`LICENSES/xslt-tools-LICENSE.txt`, the separate vendored SaxonJS licence, and
|
|
licence/notice files for every installed production npm dependency below
|
|
`LICENSES/npm/`. Generated artifacts are written below `release/` and are not
|
|
committed.
|
|
|
|
## Suggested next steps
|
|
|
|
1. Add parameter handling for `xsl:param`.
|
|
2. Add a real diagnostics/lint bridge into CodeMirror gutter markers.
|
|
3. Add transformation test cases with expected output assertions.
|
|
4. Add project import/export as a single `.xslt-tools.json` file.
|
|
5. Improve explain mode through stylesheet instrumentation.
|
|
6. Add optional precompiled SEF import/export support for larger SaxonJS projects.
|
|
|
|
## Structure
|
|
|
|
```text
|
|
src/
|
|
├── components/ # Toolbar, editor panels, dialogs, and app-specific actions
|
|
├── editor/ # CodeMirror integration and XSLT snippets
|
|
├── file/ # Local file open/save helpers
|
|
├── transform/ # Transform engine abstraction, SaxonJS, native engine
|
|
├── validation/ # XML/XSLT validation helpers
|
|
├── workspace/ # LocalStorage-backed workbench state
|
|
└── toolboxApp.ts # Toolbox manifest and shared-shell application definition
|
|
```
|
|
|
|
## Development checks
|
|
|
|
Use Node.js 22 or newer. The main local quality gate is:
|
|
|
|
```bash
|
|
npm run check
|
|
```
|
|
|
|
Useful individual commands:
|
|
|
|
```bash
|
|
npm run typecheck # TypeScript only
|
|
npm run lint # ESLint
|
|
npm run lint:fix # ESLint autofix where possible
|
|
npm run format:check # Prettier check
|
|
npm run format # Prettier write
|
|
npm run test # Vitest/jsdom test suite
|
|
npm run build # Production build
|
|
npm run smoke:static # Nested-path static build smoke test
|
|
npm run smoke:toolbox # Contract, asset, standalone, and contextual checks
|
|
npm run release # Deterministic ZIP and SHA-256 release artifacts
|
|
npm run clean # Remove generated build/cache files
|
|
```
|
|
|
|
The vendored SaxonJS browser file is intentionally excluded from ESLint and Prettier because it is a third-party distribution artifact.
|
|
|
|
## Transformation engines
|
|
|
|
The app currently exposes two engines:
|
|
|
|
- **SaxonJS 2 dynamic XSLT 3.0**: default engine. It loads the vendored `public/vendor/saxon/SaxonJS2.js`, compiles raw XSLT text to SEF in the browser, then executes the generated SEF after explicit trust confirmation.
|
|
- **Native browser XSLTProcessor**: XSLT 1.0 fallback using the browser implementation.
|
|
|
|
The SaxonJS runtime is loaded only for a trusted Saxon transformation, so the
|
|
initial app shell does not block on the large compiler/runtime file.
|