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
1 change: 1 addition & 0 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ The rest:
- `collect-all("Figure" 1 "." 3)` collects all the fields into a string. Useful for computing a link target because `target-context(...)` only allows 2 arguments
- `if(1, true-condition, false-condition)` allows you to conditionally return something. An example would be to combine this with `is-even(...)` to remove all even answers from the back of the Book
- `is-even(123)` returns `0` if the number is odd, and `111111` otherwise (truthy)
- `fetch-url('./icon.svg')` injects the contents of the file into the element
- `x-tag-name()` find out current elements' tag name (ie 'div', 'pre', 'h1')
- `x-throw()` throws an error (useful for unit tests)

Expand Down
6 changes: 6 additions & 0 deletions src/browser/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ FUNCTIONS.push(new FunctionEvaluator('attr', (evaluator, astNode, $contextEl) =>
}
return ret
}))
FUNCTIONS.push(new FunctionEvaluator('fetch-url', (evaluator, astNode, $contextEl, $) => {
const url = evaluator.evaluateFirst()
const fetchNode = $(`<transcludeduringserialization url="${url}"/>`)
fetchNode[0].__cssLocation = astNode
return fetchNode
}))
let idCounter = 0
FUNCTIONS.push(new FunctionEvaluator('x-attr-ensure-id', (evaluator, astNode, $contextEl) => {
const attrName = 'id'
Expand Down
4 changes: 2 additions & 2 deletions src/browser/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ module.exports = class Converter {
}
}

serialize (vanillaRules) {
serialize (vanillaRules, transcludedFilesMap) {
vanillaRules = csstree.fromPlainObject(vanillaRules)
return serializer(this._engine, this._htmlSourceLookup, this._htmlSourcePath, this._sourceMapPath, vanillaRules, this._htmlOutputPath, this._isXml)
return serializer(this._engine, this._htmlSourceLookup, this._htmlSourcePath, this._sourceMapPath, vanillaRules, this._htmlOutputPath, this._isXml, transcludedFilesMap)
}
}
17 changes: 16 additions & 1 deletion src/browser/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const SELF_CLOSING_TAGS = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr'
const XML_SKIP_TAGNAMES = ['html', 'head', 'body']

// We use a custom serializer so sourcemaps can be generated (we know the output line and columns for things)
module.exports = (engine, htmlSourceLookup, htmlSourcePath, htmlSourceMapPath, vanillaRules, htmlOutputPath, isXml) => {
module.exports = (engine, htmlSourceLookup, htmlSourcePath, htmlSourceMapPath, vanillaRules, htmlOutputPath, isXml, transcludedFilesMap) => {
const coverageData = {}
const documentElement = engine.getRoot()

Expand Down Expand Up @@ -148,6 +148,17 @@ module.exports = (engine, htmlSourceLookup, htmlSourcePath, htmlSourceMapPath, v
return
}

// Inject any files that should be injected (SVG icons)
if (tagName === 'transcludeduringserialization') {
const url = node.getAttribute('url')
if (!transcludedFilesMap[url]) {
throwBug(`Could not find file named ${url}`)
}
pushAndMap(node, `<!-- DEBUG: Transcluding file '${escapeHtml(url)}' -->`)
pushAndMap(node, transcludedFilesMap[url])
return
}

pushAndMap(node, `<${tagName}`)

for (let index = 0; index < node.attributes.length; index++) {
Expand Down Expand Up @@ -186,6 +197,10 @@ module.exports = (engine, htmlSourceLookup, htmlSourcePath, htmlSourceMapPath, v
if (isXml && XML_SKIP_TAGNAMES.indexOf(tagName) >= 0) {
return
}
// If the element is a _magic_ transclusion element then ignore it in the close as well
if (tagName === 'transcludeduringserialization') {
return
}
// If we have vanilla rules then we need to inject them as a <style> tag with a sourceMappingURL
if (tagName === 'head') {
if (vanillaRules) {
Expand Down
25 changes: 23 additions & 2 deletions src/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const jquery = require('jquery')
const {SourceMapConsumer, SourceMapGenerator} = require('source-map')

const renderPacket = require('./packet-render')
const {showWarning, throwBug, throwError} = require('./browser/misc/packet-builder')
const {showLog, showWarning, throwBug, throwError} = require('./browser/misc/packet-builder')
const constructSelector = require('./browser/misc/construct-selector')

const JQUERY_PATH = require.resolve('jquery')
Expand Down Expand Up @@ -399,8 +399,29 @@ async function convertNodeJS(cssPath, htmlPath, htmlOutputPath, options, packetH


vanillaRules = csstree.toPlainObject(vanillaRules)
const filesThatNeedToBeFetched = await page.evaluate(`(function () {
const urls = new Set()
const nodes = [...window.document.querySelectorAll('transcludeduringserialization')]
nodes.forEach(node => urls.add(node.getAttribute('url')))
return [...urls.values()] // return a list so it is serialized
}) ()`)
// Fetch all the SVG files
if (filesThatNeedToBeFetched.length > 0) {
showLog(`fetching... ${JSON.stringify(filesThatNeedToBeFetched)}`)
}
const transcludedFilesMap = {} // use an object so it serializes easily (rather than a Map)
for (const url of filesThatNeedToBeFetched) {
if (url.startsWith('http')) {
throwError(`fetch-url() does not support remote URLs yet: ${url}`)
}
const rootDir = path.dirname(cssPath)
const contents = await fs.readFileSync(path.join(rootDir, url), 'utf-8')
// Strip out any <?xml version="1.0" encoding="UTF-8" ?> if it exists
transcludedFilesMap[url] = contents.replace(/<\?xml.*?\?>/, '')
}

ret = await page.evaluate(`(function () {
return window.__instance.serialize(${JSON.stringify(vanillaRules)})
return window.__instance.serialize(${JSON.stringify(vanillaRules)}, ${JSON.stringify(transcludedFilesMap)})
}) ()`)
await saveCoverage()
await page.close()
Expand Down
12 changes: 12 additions & 0 deletions test/unit/svg.in.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css-plus+css">
body {
content: fetch-url('./vanilla.svg');
}
</style>
</head>
<body>

</body>
</html>
14 changes: 14 additions & 0 deletions test/unit/svg.out.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

</head>
<body><!-- DEBUG: Transcluding file './vanilla.svg' -->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect x="25" y="25" width="200" height="200" fill="lime" stroke-width="4" stroke="pink" />
<circle cx="125" cy="125" r="75" fill="orange" />
<polyline points="50,150 50,200 200,200 200,100" stroke="red" stroke-width="4" fill="none" />
<line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" />
</svg>
</body>
</html>
<!-- //# sourceMappingURL=svg.out.xhtml.map -->
Empty file added test/unit/svg.out.xhtml.txt
Empty file.