Appearance
Creative Build Pipeline
How a creative goes from JSON data to a deployable ad tag.
Overview
Frontend saves creative data (JSON)
|
Backend receives & stores creative.json
|
Backend dispatches build job
|
Creative-Composer orchestrates:
1. Copy engine files to temp workbench
2. Remap creative data (logicSettings → operators)
3. Create build profile
4. Run fileposer (inject creative data into engine)
5. Vite build (engine + data → JS bundle)
6. injectTag (post-process stub.js with engine code)
7. Generate tag.html
|
Output: built/ directory with deployable filesBuild output structure
creatives/{id}/{cuid}/
creative.json # The creative data as stored
log.txt # Build log with timestamps
tag.html # Production embed tag (loads stub.js)
tag-vast.html # VAST version for video
built/
index.html # Dev/preview HTML (unused in production)
live.html # Live preview template
assets/
creative-engine.js # Vite bundle: engine code + creative data
stub.js # Tag stub: creates iframe, injects engineKey files in the pipeline
stub.ts (Creative-Engine)
Template at Creative-Engine/templates/tagstub/stub.ts. Contains placeholders:
| Placeholder | Replaced with |
|---|---|
CREATIVEIDPLACEHOLDER | Creative ID number |
CREATIVE_GROUP_ID_PLACEHOLDER | Creative group ID |
CAMPAIGN_ID_PLACEHOLDER | Campaign ID |
BRAND_ID_PLACEHOLDER | Brand ID |
CREATIVESCRIPTSPLACEHOLDER | JSON-stringified engine JS bundle |
CREATIVETYPEPLACEHOLDER | 'banner' or 'expandable' |
BANNERWIDTHPLACEHOLDER | Width (px or undefined for fullscreen) |
BANNERHEIGHTPLACEHOLDER | Height (px or undefined for fullscreen) |
Banner.ts (Creative-Engine)
Creates an iframe with srcdoc. The engine code runs inside this iframe:
javascript
iframe.srcdoc = `
<html>
<body>
<div id='creative-${creativeId}'></div>
<script async>
const stubConf = ${JSON.stringify(this.stubConf)}
${creativeScripts} // <-- The entire engine bundle runs here
</script>
</body>
</html>
`injectTag.ts (Creative-Composer)
Post-build processor at Creative-Composer/src/bin/commands/job/injectTag.ts:
- Reads the built
stub.jsfrom vite output - Minifies it with UglifyJS
- Reads
creative-engine.jsand JSON.stringifies it - Replaces all placeholders with actual values
- Splices the engine code into
CREATIVESCRIPTSPLACEHOLDER - Writes the final
stub.jsback
Known fragility: The engine JS file is found via assets[0] — the first file returned by directory listing. If ANY static CSS file exists in the assets directory (e.g. from a <style> block in a Vue component), it will sort before creative-engine.js alphabetically and be picked up instead, breaking the build silently.
tag.html (generated by Backend)
Simple HTML wrapper that loads stub.js:
html
<script data-creative-id="{id}">
(function() {
var s = document.createElement('script');
s.src = '{baseUrl}/built/assets/stub.js?creativeInTesting=true&bust='+Date.now();
s.async = true;
document.head.appendChild(s);
})();
</script>Preview modes
Builder preview (LocalBuildPreview.vue)
Uses postMessage to send creative data directly to the engine running in an iframe. No build step required — changes are live.
Standalone preview (PreviewIframe.vue)
Triggers a full backend build, then loads the tag.html in an iframe. This exercises the complete build pipeline. The URL pattern is:
{backend}/assets/creatives/{id}/{cuid}/tag.htmlVite build configuration
The engine's vite.config.ts defines two entry points:
javascript
rollupOptions: {
input: {
'creative-engine': path.resolve(__dirname, entryFile),
'stub': path.resolve(__dirname, 'templates/tagstub/stub.ts'),
},
}A custom wrap-in-iife plugin wraps stub.js in an IIFE after build.
Troubleshooting
about:srcdoc:20 Uncaught SyntaxError: Unexpected token '{'
Symptom: Standalone preview fails. Console shows syntax error on line 20 of about:srcdoc.
Cause: A <style> block in an engine Vue component generated a .css file in the build output. injectTag.ts uses assets[0] which picked up the CSS file instead of creative-engine.js. The CSS content was injected into stub.js as JavaScript, causing a syntax error.
How to verify:
bash
# Check if a CSS file exists in the build output
ls built/assets/
# If you see creative-engine-*.css alongside creative-engine.js, that's the problem
# Check stub.js size — should be 200KB+, not ~5KB
wc -c built/assets/stub.js
# A 5KB stub.js means the engine code was NOT injectedFix: Find and remove the <style> block from the engine component. Move styles into the component's styles() computed property. See engine-styling-system.md.
Build succeeds but preview shows blank/broken creative
Check the build log: log.txt in the creative's build directory shows timing and any errors.
Check creative.json: The stored creative data is saved alongside the build. Compare it against what the frontend sends.
Rebuild: The standalone preview uses the last successful build. If you changed engine code, you need to trigger a new build from the frontend.
How to find a specific build
Build outputs are stored at:
Application-Backend/tmp/creatives/assets/creatives/{creativeId}/{cuid}/Sort by modification time to find the most recent build for a creative. Each creative may have multiple builds (different cuids).
Important constraints
- No
<style>blocks in engine components — generates CSS files that breakinjectTag.ts - All styling via
styles()computed — seeengine-styling-system.md assets[0]fragility — the Composer assumes only JS files in the assets directory- Form blocks bypass the remapper — they pass through
createPayload/getCreativeObjectuntouched ascreativeBlocks, unlike operators which go throughlogicSettings