-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cf099f0
commit 8b8c847
Showing
16 changed files
with
433 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
Always omit | ||
<!-- routify:meta omit --> | ||
Omit in production | ||
<!-- routify:meta omit="production" --> | ||
Omit in multiple environments | ||
<!-- routify:meta omit=["production", "staging"] | ||
*/ | ||
|
||
const coerceArray = val => (Array.isArray(val) ? val : [val]) | ||
const crossMatch = (a, b) => a.some(x => b.includes(x)) | ||
|
||
/** @type {RoutifyBuildtimePlugin} */ | ||
export const omitNodePlugin = { | ||
name: 'omitNode', | ||
after: 'filemapper', | ||
before: 'exporter', | ||
build: ({ instance }) => { | ||
instance.nodeIndex.forEach(node => { | ||
const omits = coerceArray(node.meta.omit) | ||
const conditions = [process.env.NODE_ENV, true] | ||
if (crossMatch(omits, conditions)) node.remove() | ||
}) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { themes } from './themes.js' | ||
|
||
/** | ||
* @type {RoutifyBuildtimePlugin} | ||
**/ | ||
export const themesPlugin = { | ||
name: 'themesPlugin', | ||
after: 'metaFromFile', | ||
before: 'exporter', | ||
build: async ctx => { | ||
themes(ctx) | ||
}, | ||
} |
1 change: 1 addition & 0 deletions
1
lib/buildtime/plugins/themes/spec/example/@_lang/@at/blog/index.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
@at/blog/index |
1 change: 1 addition & 0 deletions
1
lib/buildtime/plugins/themes/spec/example/@_lang/@at/index.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
austrian index |
1 change: 1 addition & 0 deletions
1
lib/buildtime/plugins/themes/spec/example/@_lang/@de/@xmas/dexmasonly.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
german xmas only index |
1 change: 1 addition & 0 deletions
1
lib/buildtime/plugins/themes/spec/example/@_lang/@de/@xmas/index.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
german xmas index |
1 change: 1 addition & 0 deletions
1
lib/buildtime/plugins/themes/spec/example/@_lang/@de/index.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
german index |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
english about |
1 change: 1 addition & 0 deletions
1
lib/buildtime/plugins/themes/spec/example/blog/@de/index.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
blog/@de/index |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
blog index |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
english index |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import { dirname } from 'path' | ||
import { fileURLToPath } from 'url' | ||
import fse from 'fs-extra' | ||
import { filemapper } from '../../../../buildtime/plugins/filemapper/lib/index.js' | ||
import { RoutifyBuildtime } from '../../../../buildtime/RoutifyBuildtime.js' | ||
import { themes } from '../themes.js' | ||
import { normalizeConfig, normalizePreset } from '../utils.js' | ||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url)) | ||
|
||
const options = { | ||
routifyDir: `${__dirname}/temp`, | ||
routesDir: { default: `${__dirname}/example` }, | ||
} | ||
|
||
beforeEach(() => { | ||
fse.emptyDirSync(options.routifyDir) | ||
}) | ||
|
||
describe('theme config', () => { | ||
test('presets', () => { | ||
const defaultPreset = { | ||
preferences: [], | ||
namespaces: [/^@_/], | ||
rootNodes: [], | ||
} | ||
test('can handle shorthand ', () => { | ||
const input = [['de', 'xmas'], 'de'] | ||
const expected = { | ||
rootNodes: [], | ||
namespaces: [/^@_/], | ||
preferences: [['de', 'xmas'], ['de']], | ||
} | ||
|
||
const normalizedPreset = normalizePreset(input, defaultPreset) | ||
expect(normalizedPreset).toEqual(expected) | ||
}) | ||
|
||
test('can handle full preset', () => { | ||
const input = { | ||
preferences: [['de', 'xmas'], ['de']], | ||
namespaces: ['foo'], | ||
rootNodes: ['blog'], | ||
} | ||
|
||
const normalizedPreset = normalizePreset(input, defaultPreset) | ||
expect(normalizedPreset).toEqual(input) | ||
}) | ||
}) | ||
test('config', () => { | ||
test('no defaults', () => { | ||
const input = { presets: { at: [['at', 'xmas'], 'at'] } } | ||
|
||
const normalizedConfig = normalizeConfig(input, []) | ||
expect(normalizedConfig).toEqual({ | ||
presets: { | ||
at: { | ||
rootNodes: [], | ||
namespaces: [/^@_/], | ||
preferences: [['at', 'xmas'], ['at']], | ||
}, | ||
}, | ||
defaults: { | ||
file: 'en', | ||
app: 'en', | ||
namespaces: [/^@_/], | ||
rootNodes: [], | ||
}, | ||
}) | ||
}) | ||
test('with defaults', () => { | ||
const input = { | ||
presets: { at: [['at', 'xmas'], 'at'] }, | ||
defaults: { file: 'de', namespaces: ['foo'] }, | ||
} | ||
|
||
const normalizedConfig = normalizeConfig(input, []) | ||
expect(normalizedConfig).toEqual({ | ||
presets: { | ||
at: { | ||
rootNodes: [], | ||
namespaces: ['foo'], | ||
preferences: [['at', 'xmas'], ['at']], | ||
}, | ||
}, | ||
defaults: { | ||
file: 'de', | ||
app: 'en', | ||
namespaces: ['foo'], | ||
rootNodes: [], | ||
}, | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('themes', async () => { | ||
const themeConfig = { | ||
presets: { | ||
'at-xmas': [['at', 'xmas'], ['de', 'xmas'], 'at', 'de', ['en', 'xmas'], 'en'], | ||
at: ['at', 'de', 'en'], | ||
'de-xmas': [['de', 'xmas'], 'de', ['en', 'xmas'], 'en'], | ||
de: ['de', 'en'], | ||
'en-xmas': [['en', 'xmas'], 'en'], | ||
en: ['en'], | ||
}, | ||
} | ||
const instance = new RoutifyBuildtime({ ...options, themes: themeConfig }) | ||
await filemapper({ instance }) | ||
await themes({ instance }) | ||
|
||
test('creates themed root nodes', () => { | ||
// Object.entries(instance.rootNodes).forEach(([name, rootNode]) => { | ||
// console.log('THEME: ' + name) | ||
// // console.log(rootNode) | ||
// visualNodeTree(rootNode) | ||
// }) | ||
expect(instance.rootNodes['default-theme-at-xmas']).toBeTruthy() | ||
expect(instance.rootNodes['default-theme-de-xmas']).toBeTruthy() | ||
expect(instance.rootNodes['default-theme-en-xmas']).toBeTruthy() | ||
expect(instance.rootNodes['default-theme-at']).toBeTruthy() | ||
expect(instance.rootNodes['default-theme-de']).toBeTruthy() | ||
expect(instance.rootNodes['default-theme-en']).toBeTruthy() | ||
expect(instance.rootNodes['default']).toBeTruthy() | ||
}) | ||
|
||
test('can have nested folders in a theme', () => { | ||
const node = instance.rootNodes['default-theme-at-xmas'].traverse('/blog/index') | ||
expect(node).toBeTruthy() | ||
expect(node.id).toBe('_default___lang__at_blog_index_svelte') | ||
expect(node.file.dir).toEqual( | ||
'lib/buildtime/plugins/themes/spec/example/@_lang/@at/blog', | ||
) | ||
}) | ||
|
||
test('themes can be nested in folders', () => { | ||
const node = instance.rootNodes['default-theme-de-xmas'].traverse('/blog/index') | ||
expect(node).toBeTruthy() | ||
expect(node.id).toBe('_default_blog__de_index_svelte') | ||
expect(node.file.dir).toEqual( | ||
'lib/buildtime/plugins/themes/spec/example/blog/@de', | ||
) | ||
}) | ||
|
||
test('themes can have precedence', () => { | ||
const indexNode = instance.rootNodes['default-theme-at-xmas'].traverse('/index') | ||
expect(indexNode.file.dir).toEqual( | ||
'lib/buildtime/plugins/themes/spec/example/@_lang/@de/@xmas', | ||
) | ||
|
||
const blogNode = | ||
instance.rootNodes['default-theme-at-xmas'].traverse('/blog/index') | ||
expect(blogNode.file.dir).toEqual( | ||
'lib/buildtime/plugins/themes/spec/example/@_lang/@at/blog', | ||
) | ||
}) | ||
|
||
test('unwanted themes are excluded', () => { | ||
const yes = instance.rootNodes['default-theme-de-xmas'].traverse('/dexmasonly') | ||
const no = instance.rootNodes['default-theme-de'].traverse('/dexmasonly', { | ||
silent: true, | ||
}) | ||
expect(yes).toBeTruthy() | ||
expect(no).toBeFalsy() | ||
}) | ||
}) | ||
|
||
describe('themes can be built', async () => { | ||
const themeConfig = { | ||
presets: { | ||
'at-xmas': [['at', 'xmas'], ['de', 'xmas'], 'at', 'de', ['en', 'xmas'], 'en'], | ||
at: ['at', 'de', 'en'], | ||
'de-xmas': [['de', 'xmas'], 'de', ['en', 'xmas'], 'en'], | ||
de: ['de', 'en'], | ||
'en-xmas': [['en', 'xmas'], 'en'], | ||
en: ['en'], | ||
}, | ||
} | ||
const instance = new RoutifyBuildtime({ ...options, themes: themeConfig }) | ||
await instance.build() | ||
}) | ||
|
||
const visualNodeTree = (node, depth = 0) => { | ||
const indent = ' '.repeat(depth) | ||
console.log(`${indent}${node.name} - ${node.meta.__themesPlugin_themes?.join(', ')}`) | ||
node.children.forEach(child => visualNodeTree(child, depth + 1)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { coerceArray, arraysAreEqual, normalizeConfig } from './utils.js' | ||
Check failure on line 1 in lib/buildtime/plugins/themes/themes.js GitHub Actions / Test on NodeJS v14.x
|
||
|
||
export const themes = async ({ instance }) => { | ||
const config = normalizeConfig( | ||
instance.options.themes, | ||
Object.keys(instance.rootNodes), | ||
) | ||
createThemedRootNodes(instance, config) | ||
} | ||
|
||
/** | ||
* @param {RoutifyBuildtime} instance | ||
* @param {import('./utils.js').ThemeConfig} config | ||
*/ | ||
export const createThemedRootNodes = (instance, config) => { | ||
// remove theme folders | ||
instance.nodeIndex = instance.nodeIndex.filter(node => !node.name.startsWith('@')) | ||
// tag nodes | ||
instance.nodeIndex.forEach(tagNodeThemes) | ||
|
||
Object.entries(config.presets).forEach(([name, themePreset]) => { | ||
themePreset.rootNodes.forEach(rootNodeName => { | ||
createThemedRootNode(instance, name, themePreset.preferences, rootNodeName) | ||
}) | ||
}) | ||
|
||
// clean up meta props | ||
instance.nodeIndex.forEach(node => { | ||
delete node.meta.__themesPlugin_themes | ||
delete node.meta.__themesPlugin_parentPath | ||
}) | ||
} | ||
|
||
/** | ||
* | ||
* @param {RoutifyBuildtime} instance | ||
* @param {(string[]|string)[]} themePreferenceGroups | ||
*/ | ||
export const createThemedRootNode = ( | ||
instance, | ||
name, | ||
themePreferenceGroups, | ||
rootNodeName, | ||
) => { | ||
const rootNode = instance.rootNodes[rootNodeName].deepClone() | ||
rootNode.name = name | ||
rootNode.id = '_' + name | ||
rootNode.rootName = rootNodeName + '-theme-' + name | ||
instance.rootNodes[rootNodeName + '-theme-' + name] = rootNode | ||
;[...themePreferenceGroups].reverse().forEach(themePreferenceGroup => { | ||
themePreferenceGroup = coerceArray(themePreferenceGroup) | ||
instance.nodeIndex | ||
.filter(node => nodeMatchesThemes(node, themePreferenceGroup)) | ||
.forEach(copyNodeToTheme(rootNode)) | ||
}) | ||
// return rootNodes | ||
} | ||
|
||
export const nodeMatchesThemes = (node, themes) => | ||
node.meta.__themesPlugin_themes && | ||
arraysAreEqual(node.meta.__themesPlugin_themes, themes) | ||
|
||
export const copyNodeToTheme = rootNode => node => | ||
copyNode(node, rootNode, node.meta.__themesPlugin_parentPath || '/') | ||
|
||
/** | ||
* @param {RNode} node | ||
* @param {string} location | ||
*/ | ||
export const copyNode = (node, rootNode, location = '/') => { | ||
const newNode = node.clone() | ||
const parent = rootNode.traverse(location) | ||
// remove existing node | ||
parent.children.find(child => child.name === newNode.name)?.remove() | ||
|
||
parent.appendChild(newNode) | ||
} | ||
|
||
/** | ||
* @param {RNode} node | ||
*/ | ||
export const tagNodeThemes = node => { | ||
const allTags = [...node.ancestors].reverse().map(a => a.name) | ||
|
||
const themes = allTags | ||
.filter(tag => tag.startsWith('@') && !tag.startsWith('@_')) | ||
.map(tag => tag.slice(1)) | ||
const path = allTags.filter(tag => !tag.startsWith('@')).join('/') | ||
node.meta.__themesPlugin_themes = themes | ||
node.meta.__themesPlugin_parentPath = path | ||
// node.meta.__themesPlugin_selfPath = (path + '/' + node.name).replace(/\/+/, '/') | ||
} |
Oops, something went wrong.