diff options
-rw-r--r-- | src/content/dependencies/generateStaticPage.js | 11 | ||||
-rw-r--r-- | src/content/dependencies/transformContent.js | 11 | ||||
-rw-r--r-- | src/data/composite/things/album/withTrackSections.js | 4 | ||||
-rw-r--r-- | src/data/composite/things/album/withTracks.js | 4 | ||||
-rw-r--r-- | src/data/things/album.js | 5 | ||||
-rw-r--r-- | src/data/things/static-page.js | 1 | ||||
-rw-r--r-- | src/data/things/track.js | 13 | ||||
-rw-r--r-- | src/data/yaml.js | 12 | ||||
-rw-r--r-- | src/static/client3.js | 92 | ||||
-rw-r--r-- | src/util/sugar.js | 35 | ||||
-rw-r--r-- | tap-snapshots/test/snapshot/transformContent.js.test.cjs | 39 | ||||
-rw-r--r-- | test/lib/wiki-data.js | 35 | ||||
-rw-r--r-- | test/snapshot/transformContent.js | 39 | ||||
-rw-r--r-- | test/unit/data/things/album.js | 20 | ||||
-rw-r--r-- | test/unit/data/things/track.js | 16 |
15 files changed, 199 insertions, 138 deletions
diff --git a/src/content/dependencies/generateStaticPage.js b/src/content/dependencies/generateStaticPage.js index 3e27fd43..226152c7 100644 --- a/src/content/dependencies/generateStaticPage.js +++ b/src/content/dependencies/generateStaticPage.js @@ -1,5 +1,6 @@ export default { contentDependencies: ['generatePageLayout', 'transformContent'], + extraDependencies: ['html'], relations(relation, staticPage) { return { @@ -12,10 +13,11 @@ export default { return { name: staticPage.name, stylesheet: staticPage.stylesheet, + script: staticPage.script, }; }, - generate(data, relations) { + generate(data, relations, {html}) { return relations.layout .slots({ title: data.name, @@ -27,7 +29,12 @@ export default { : []), mainClasses: ['long-content'], - mainContent: relations.content, + mainContent: [ + relations.content, + + data.script && + html.tag('script', data.script), + ], navLinkStyle: 'hierarchical', navLinks: [ diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js index b0a7796c..2002ebee 100644 --- a/src/content/dependencies/transformContent.js +++ b/src/content/dependencies/transformContent.js @@ -562,11 +562,14 @@ export default { const transformMultiline = () => { const markedInput = extractNonTextNodes() - // Compress multiple line breaks into single line breaks. - .replace(/\n{2,}/g, '\n') + // Compress multiple line breaks into single line breaks, + // except when they're preceding or following indented + // text (by at least two spaces). + .replace(/(?<! .*)\n{2,}(?!^ )/gm, '\n') /* eslint-disable-line no-regex-spaces */ // Expand line breaks which don't follow a list, quote, - // or <br> / " ". - .replace(/(?<!^ *-.*|^>.*| $|<br>$)\n+/gm, '\n\n') /* eslint-disable-line no-regex-spaces */ + // or <br> / " ", and which don't precede or follow + // indented text (by at least two spaces). + .replace(/(?<!^ *-.*|^>.*|^ .*\n*| $|<br>$)\n+(?! |\n)/gm, '\n\n') /* eslint-disable-line no-regex-spaces */ // Expand line breaks which are at the end of a list. .replace(/(?<=^ *-.*)\n+(?!^ *-)/gm, '\n\n') // Expand line breaks which are at the end of a quote. diff --git a/src/data/composite/things/album/withTrackSections.js b/src/data/composite/things/album/withTrackSections.js index baa3cb4a..679a09fd 100644 --- a/src/data/composite/things/album/withTrackSections.js +++ b/src/data/composite/things/album/withTrackSections.js @@ -22,7 +22,7 @@ export default templateCompositeFrom({ steps: () => [ exitWithoutDependency({ - dependency: 'trackData', + dependency: 'ownTrackData', value: input.value([]), }), @@ -75,7 +75,7 @@ export default templateCompositeFrom({ withResolvedReferenceList({ list: '#trackRefs', - data: 'trackData', + data: 'ownTrackData', notFoundMode: input.value('null'), find: input.value(find.track), }).outputs({ diff --git a/src/data/composite/things/album/withTracks.js b/src/data/composite/things/album/withTracks.js index dcea6593..fff3d5ae 100644 --- a/src/data/composite/things/album/withTracks.js +++ b/src/data/composite/things/album/withTracks.js @@ -12,7 +12,7 @@ export default templateCompositeFrom({ steps: () => [ exitWithoutDependency({ - dependency: 'trackData', + dependency: 'ownTrackData', value: input.value([]), }), @@ -35,7 +35,7 @@ export default templateCompositeFrom({ withResolvedReferenceList({ list: '#trackRefs', - data: 'trackData', + data: 'ownTrackData', find: input.value(find.track), }), diff --git a/src/data/things/album.js b/src/data/things/album.js index 63ec1140..a95ba354 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -133,7 +133,10 @@ export class Album extends Thing { class: input.value(Group), }), - trackData: wikiData({ + // Only the tracks which belong to this album. + // Necessary for computing the track list, so provide this statically + // or keep it updated. + ownTrackData: wikiData({ class: input.value(Track), }), diff --git a/src/data/things/static-page.js b/src/data/things/static-page.js index ab9c5f98..8a3fd10e 100644 --- a/src/data/things/static-page.js +++ b/src/data/things/static-page.js @@ -30,5 +30,6 @@ export class StaticPage extends Thing { directory: directory(), content: simpleString(), stylesheet: simpleString(), + script: simpleString(), }); } diff --git a/src/data/things/track.js b/src/data/things/track.js index 08891719..e3fe0804 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -336,12 +336,21 @@ export class Track extends Thing { } let album; - if (depth >= 0 && (album = this.album ?? this.dataSourceAlbum)) { + + if (depth >= 0) { + try { + album = this.album; + } catch (_error) {} + + album ??= this.dataSourceAlbum; + } + + if (album) { const albumName = album.name; const albumIndex = album.tracks.indexOf(this); const trackNum = (albumIndex === -1 - ? '#?' + ? 'indeterminate position' : `#${albumIndex + 1}`); parts.push(` (${colors.yellow(trackNum)} in ${colors.green(albumName)})`); } diff --git a/src/data/yaml.js b/src/data/yaml.js index 2c600341..82b7faf2 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -646,6 +646,7 @@ export const processStaticPageDocument = makeProcessDocument(T.StaticPage, { directory: 'Directory', stylesheet: 'Style', + script: 'Script', content: 'Content', }, }); @@ -935,6 +936,7 @@ export const dataSteps = [ // an individual section before applying it, since those are just // generic objects; they aren't Things in and of themselves.) const trackSections = []; + const ownTrackData = []; let currentTrackSection = { name: `Default Track Section`, @@ -969,13 +971,16 @@ export const dataSteps = [ entry.dataSourceAlbum = albumRef; + ownTrackData.push(entry); currentTrackSection.tracks.push(Thing.getReference(entry)); } closeCurrentTrackSection(); - album.trackSections = trackSections; albumData.push(album); + + album.trackSections = trackSections; + album.ownTrackData = ownTrackData; } return {albumData, trackData}; @@ -1550,7 +1555,7 @@ export function linkWikiDataArrays(wikiData, { assignWikiData([WD.wikiInfo], 'groupData'); - assignWikiData(WD.albumData, 'artistData', 'artTagData', 'groupData', 'trackData'); + assignWikiData(WD.albumData, 'artistData', 'artTagData', 'groupData'); assignWikiData(WD.trackData, 'albumData', 'artistData', 'artTagData', 'flashData', 'trackData'); assignWikiData(WD.artistData, 'albumData', 'artistData', 'flashData', 'trackData'); assignWikiData(WD.groupData, 'albumData', 'groupCategoryData'); @@ -1624,8 +1629,7 @@ export function filterDuplicateDirectories(wikiData) { call(() => { throw new Error( `Duplicate directory ${colors.green(directory)}:\n` + - places.map((thing) => ` - ` + inspect(thing)).join('\n') - ); + places.map(thing => ` - ` + inspect(thing)).join('\n')); }); } diff --git a/src/static/client3.js b/src/static/client3.js index 86b5f985..1e64ebe1 100644 --- a/src/static/client3.js +++ b/src/static/client3.js @@ -2015,98 +2015,6 @@ function addExternalIconTooltipPageListeners() { clientSteps.getPageReferences.push(getExternalIconTooltipReferences); clientSteps.addPageListeners.push(addExternalIconTooltipPageListeners); -/* -const linkIconTooltipInfo = - Array.from(document.querySelectorAll('span.contribution.has-tooltip')) - .map(span => ({ - mainLink: span.querySelector('a'), - iconsContainer: span.querySelector('span.icons-tooltip'), - iconLinks: span.querySelectorAll('span.icons-tooltip a'), - })); - -for (const info of linkIconTooltipInfo) { - const focusElements = - [info.mainLink, ...info.iconLinks]; - - const hoverElements = - [info.mainLink, info.iconsContainer]; - - let hidden = true; - - const show = () => { - info.iconsContainer.classList.add('visible'); - info.iconsContainer.inert = false; - hidden = false; - }; - - const hide = () => { - info.iconsContainer.classList.remove('visible'); - info.iconsContainer.inert = true; - hidden = true; - }; - - const considerHiding = () => { - if (hoverElements.some(el => el.matches(':hover'))) { - return; - } - - if (<document.activeElement is inside tooltip>) { - return; - } - - if (justTouched) { - return; - } - - hide(); - }; - - // Hover (pointer) - - let hoverTimeout; - - info.mainLink.addEventListener('mouseenter', () => { - if (hidden) { - hoverTimeout = setTimeout(show, 250); - } - }); - - info.mainLink.addEventListener('mouseout', () => { - if (hidden) { - clearTimeout(hoverTimeout); - } else { - considerHiding(); - } - }); - - info.iconsContainer.addEventListener('mouseout', () => { - if (!hidden) { - considerHiding(); - } - }); - - // Focus (keyboard) - - let focusTimeout; - - info.mainLink.addEventListener('focus', () => { - focusTimeout = setTimeout(show, 750); - }); - - info.mainLink.addEventListener('blur', () => { - clearTimeout(focusTimeout); - }); - - info.iconsContainer.addEventListener('focusout', () => { - requestAnimationFrame(considerHiding); - }); - - info.mainLink.addEventListener('blur', () => { - requestAnimationFrame(considerHiding); - }); -} -*/ - // Sticky commentary sidebar ------------------------------ const albumCommentarySidebarInfo = clientInfo.albumCommentarySidebarInfo = { diff --git a/src/util/sugar.js b/src/util/sugar.js index eab44b75..cee3df12 100644 --- a/src/util/sugar.js +++ b/src/util/sugar.js @@ -589,6 +589,32 @@ export function _withAggregate(mode, aggregateOpts, fn) { } } +export const unhelpfulStackLines = [ + /sugar/, + /node:/, + /<anonymous>/, +]; + +export function getUsefulStackLine(stack) { + if (!stack) return ''; + + function isUseful(stackLine) { + const trimmed = stackLine.trim(); + + if (!trimmed.startsWith('at')) + return false; + + if (unhelpfulStackLines.some(regex => regex.test(trimmed))) + return false; + + return true; + } + + const stackLines = stack.split('\n'); + const usefulStackLine = stackLines.find(isUseful); + return usefulStackLine ?? ''; +} + export function showAggregate(topError, { pathToFileURL = f => f, showTraces = true, @@ -670,15 +696,8 @@ export function showAggregate(topError, { : messagePart); if (showTraces) { - const stackLines = - stack?.split('\n'); - const stackLine = - stackLines?.find(line => - line.trim().startsWith('at') && - !line.includes('sugar') && - !line.includes('node:') && - !line.includes('<anonymous>')); + getUsefulStackLine(stack); const tracePart = (stackLine diff --git a/tap-snapshots/test/snapshot/transformContent.js.test.cjs b/tap-snapshots/test/snapshot/transformContent.js.test.cjs index 85ee740f..9ab299e6 100644 --- a/tap-snapshots/test/snapshot/transformContent.js.test.cjs +++ b/tap-snapshots/test/snapshot/transformContent.js.test.cjs @@ -10,6 +10,45 @@ exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > <p>Very nice: <time datetime="Fri, 25 Oct 2413 03:00:00 GMT">10/25/2413</time></p> ` +exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > hanging indent list 1`] = ` +<p>Hello!</p> +<ul> +<li><p>I am a list item and I +go on and on and on +and on and on and on.</p> +</li> +<li><p>I am another list item. +Yeah.</p> +</li> +</ul> +<p>In-between!</p> +<ul> +<li>Spooky, +spooky, I say!</li> +<li>Following list item. +No empty line around me.</li> +<li>Very cool. +So, so cool.</li> +</ul> +<p>Goodbye!</p> +` + +exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > indent on a directly following line 1`] = ` +<div> + <span>Wow!</span> +</div> +` + +exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > indent on an indierctly following line 1`] = ` +<p>Some text.</p> +<p>Yes, some more text.</p> +<pre><code>I am hax0rz!! +All yor base r blong 2 us. +</code></pre> +<p>Aye.</p> +<p>Aye aye aye.</p> +` + exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > inline images 1`] = ` <p><img src="snooping.png"> as USUAL...</p> <p>What do you know? <img src="cowabunga.png" width="24" height="32"></p> diff --git a/test/lib/wiki-data.js b/test/lib/wiki-data.js index c4083a56..5433de29 100644 --- a/test/lib/wiki-data.js +++ b/test/lib/wiki-data.js @@ -1,7 +1,32 @@ +import CacheableObject from '#cacheable-object'; +import find from '#find'; import {linkWikiDataArrays} from '#yaml'; -export function linkAndBindWikiData(wikiData) { - linkWikiDataArrays(wikiData); +export function linkAndBindWikiData(wikiData, { + inferAlbumsOwnTrackData = true, +} = {}) { + function customLinkWikiDataArrays(...args) { + linkWikiDataArrays(...args); + + // If albumData is present, automatically set albums' ownTrackData values + // by resolving track sections' references against the full array. This is + // just a nicety for working with albums throughout tests. + if (inferAlbumsOwnTrackData && wikiData.albumData && wikiData.trackData) { + for (const album of wikiData.albumData) { + const trackSections = + CacheableObject.getUpdateValue(album, 'trackSections'); + + const trackRefs = + trackSections.flatMap(section => section.tracks); + + album.ownTrackData = + trackRefs.map(ref => + find.track(ref, wikiData.trackData, {mode: 'error'})); + } + } + } + + customLinkWikiDataArrays(wikiData); return { // Mutate to make the below functions aware of new data objects, or of @@ -13,12 +38,14 @@ export function linkAndBindWikiData(wikiData) { // It'll automatically relink everything on wikiData so all the objects // are caught up to date. linkWikiDataArrays: - linkWikiDataArrays.bind(null, wikiData), + customLinkWikiDataArrays + .bind(null, wikiData), // Use this if you HAVEN'T mutated wikiData and just need to decache // indirect dependencies on exposed properties of other data objects. // See documentation on linkWikiDataArarys (in yaml.js) for more info. XXX_decacheWikiData: - linkWikiDataArrays.bind(null, wikiData, {XXX_decacheWikiData: true}), + customLinkWikiDataArrays + .bind(null, wikiData, {XXX_decacheWikiData: true}), }; } diff --git a/test/snapshot/transformContent.js b/test/snapshot/transformContent.js index b05beac1..740d94df 100644 --- a/test/snapshot/transformContent.js +++ b/test/snapshot/transformContent.js @@ -37,6 +37,45 @@ testContentFunctions(t, 'transformContent (snapshot)', async (t, evaluate) => { `That's right, [[album:cool-album]]!`); quickSnapshot( + 'indent on a directly following line', + `<div>\n` + + ` <span>Wow!</span>\n` + + `</div>`); + + quickSnapshot( + 'indent on an indierctly following line', + `Some text.\n` + + `Yes, some more text.\n` + + `\n` + + ` I am hax0rz!!\n` + + ` All yor base r blong 2 us.\n` + + `\n` + + `Aye.\n` + + `Aye aye aye.`); + + quickSnapshot( + 'hanging indent list', + `Hello!\n` + + `\n` + + `* I am a list item and I\n` + + ` go on and on and on\n` + + ` and on and on and on.\n` + + `\n` + + `* I am another list item.\n` + + ` Yeah.\n` + + `\n` + + `In-between!\n` + + `\n` + + `* Spooky,\n` + + ` spooky, I say!\n` + + `* Following list item.\n` + + ` No empty line around me.\n` + + `* Very cool.\n` + + ` So, so cool.\n` + + `\n` + + `Goodbye!`); + + quickSnapshot( 'inline images', `<img src="snooping.png"> as USUAL...\n` + `What do you know? <img src="cowabunga.png" width="24" height="32">\n` + diff --git a/test/unit/data/things/album.js b/test/unit/data/things/album.js index 76a2b90f..5a1261a4 100644 --- a/test/unit/data/things/album.js +++ b/test/unit/data/things/album.js @@ -204,11 +204,13 @@ t.test(`Album.tracks`, t => { const track1 = stubTrack('track1'); const track2 = stubTrack('track2'); const track3 = stubTrack('track3'); + const tracks = [track1, track2, track3]; - linkAndBindWikiData({ - albumData: [album], - trackData: [track1, track2, track3], - }); + album.ownTrackData = tracks; + + for (const track of tracks) { + track.albumData = [album]; + } t.same(album.tracks, [], `Album.tracks #1: defaults to empty array`); @@ -259,11 +261,13 @@ t.test(`Album.trackSections`, t => { const track2 = stubTrack('track2'); const track3 = stubTrack('track3'); const track4 = stubTrack('track4'); + const tracks = [track1, track2, track3, track4]; - linkAndBindWikiData({ - albumData: [album], - trackData: [track1, track2, track3, track4], - }); + album.ownTrackData = tracks; + + for (const track of tracks) { + track.albumData = [album]; + } album.trackSections = [ {tracks: ['track:track1', 'track:track2']}, diff --git a/test/unit/data/things/track.js b/test/unit/data/things/track.js index f84ba1cb..57bd4253 100644 --- a/test/unit/data/things/track.js +++ b/test/unit/data/things/track.js @@ -80,8 +80,8 @@ t.test(`Track.album`, t => { track1.albumData = [album1, album2]; track2.albumData = [album1, album2]; - album1.trackData = [track1, track2]; - album2.trackData = [track1, track2]; + album1.ownTrackData = [track1, track2]; + album2.ownTrackData = [track1, track2]; album1.trackSections = [{tracks: ['track:track1']}]; album2.trackSections = [{tracks: ['track:track2']}]; @@ -98,13 +98,13 @@ t.test(`Track.album`, t => { t.equal(track1.album, null, `album #4: is null when track missing albumData`); - album1.trackData = []; + album1.ownTrackData = []; track1.albumData = [album1, album2]; t.equal(track1.album, null, - `album #5: is null when album missing trackData`); + `album #5: is null when album missing ownTrackData`); - album1.trackData = [track1, track2]; + album1.ownTrackData = [track1, track2]; album1.trackSections = [{tracks: ['track:track2']}]; // XXX_decacheWikiData @@ -165,7 +165,7 @@ t.test(`Track.color`, t => { const {track, album} = stubTrackAndAlbum(); - const {wikiData, linkWikiDataArrays, XXX_decacheWikiData} = linkAndBindWikiData({ + const {XXX_decacheWikiData} = linkAndBindWikiData({ albumData: [album], trackData: [track], }); @@ -188,7 +188,7 @@ t.test(`Track.color`, t => { // track's album will always have a corresponding track section. But if that // connection breaks for some future reason (with the album still present), // Track.color should still inherit directly from the album. - wikiData.albumData = [ + track.albumData = [ { constructor: {[Thing.referenceType]: 'album'}, color: '#abcdef', @@ -199,8 +199,6 @@ t.test(`Track.color`, t => { }, ]; - linkWikiDataArrays(); - t.equal(track.color, '#abcdef', `color #3: inherits from album without matching track section`); |