185 lines
8.1 KiB
Markdown
185 lines
8.1 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. The shared top bar keeps the app title centered and provides Help, its Gitea source link, persistent System/Light/Dark personalization, and app selection. When launched with a same-origin `?toolbox=<catalog URL>` context, the Apps menu includes the catalogue tools; 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
|
|
- theme-aware light and dark editor, gutter, and highlighting palettes
|
|
- 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, unified Help/source controls, personalization, and application switching
|
|
- 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.
|
|
|
|
## Appearance
|
|
|
|
The stable shared top bar keeps Personalize, Apps, the XSLT source link, and Help in a fixed left cluster, the app title and one tagline centered, and the Toolbox icon with the `add·ideas Toolbox` label in a fixed far-right block. Use **Personalize** to select System, Light, or Dark. The choice is stored only in this browser using the same preference record as the Toolbox Portal, so it follows you between current Toolbox apps on the same origin. System mode follows the operating-system color preference. CodeMirror is reconfigured when the effective palette changes, including its editor background, gutters, active line, and XML syntax colors.
|
|
|
|
## 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.
|