-
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
9447efa
commit e31dbdd
Showing
11 changed files
with
280 additions
and
60 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/** | ||
* @typedef {Object} FlexMapPluginOptions | ||
* @property {Object<string, string[]>} [variationsMap] - an array of variations for each route dir. | ||
*/ | ||
|
||
/** | ||
* This plugin generates a route file for each specified variation in the `variationsMap` option. | ||
* | ||
* Example: | ||
* If `variationsMap` is set to `{ 'default': ['en', 'de'] }`, the plugin will create two additional | ||
* route files: `routes.default.en.js` and `routes.default.de.js`. | ||
* | ||
* Usage: | ||
* - Components specific to a variation should be suffixed with the variation's key. | ||
* For example, a German variation of the Home component should be named `Home.de.svelte`. | ||
* | ||
* Fallback Variations: | ||
* - Specify fallback variations using a comma-separated list within the array. | ||
* For example, `['en-us,en-uk']` sets up a priority order for route resolution: | ||
* 1. First, Routify 3 will look for a component suffixed with `.en-us.svelte` (e.g., `Home.en-us.svelte`). | ||
* 2. If the `.en-us` variant is not available, it will then try the `.en-uk` variant (e.g., `Home.en-uk.svelte`). | ||
* 3. If neither variant is available, Routify 3 will default to the base component (e.g., `Home.svelte`). | ||
* | ||
* This setup ensures that Routify 3 searches for the most specific variation first, falling back to more general | ||
* variations if the specific ones are not found, thereby providing a flexible and efficient way to manage route variations. | ||
* | ||
* This approach allows for flexible and organized management of route variations based on | ||
* language, theme, or other factors, enhancing the adaptability of your Routify application. | ||
* | ||
* @example | ||
* const routifyConfig = { | ||
* plugins: [ | ||
* i18nPlugin({ | ||
* variationsMap: { | ||
* default: ['en-us,en','en', 'de'], | ||
* widget: ['en', 'de'], | ||
* } | ||
* }) | ||
* ], | ||
* routesDir: { | ||
* 'default': 'src/routes', | ||
* 'widget': 'src/widget' | ||
* }, | ||
* } | ||
* @param {Partial<FlexMapPluginOptions>} options | ||
* @returns {RoutifyBuildtimePlugin[]} | ||
*/ | ||
export const flexMapsPlugin = options => [ | ||
flexMapsPluginInit(options), | ||
flexMapsNormalize(options), | ||
] | ||
|
||
/** | ||
* Creates a route dir for each variation in the `variationsMap` option. | ||
* Eg. if `variationsMap` is `{ 'default': ['en', 'de'] }`, the plugin will create | ||
* a `routes.default.en.js` and a `routes.default.de.js` file. | ||
* @params {Partial<FlexMapPluginOptions>} options | ||
* @returns {RoutifyBuildtimePlugin} | ||
*/ | ||
function flexMapsPluginInit(options) { | ||
return { | ||
name: 'flexMapPlugin-createRouteDirs', | ||
before: 'filemapper', | ||
build: async ctx => { | ||
let { routesDir } = ctx.instance.options | ||
Object.entries(options.variationsMap).forEach(([routeDir, variations]) => { | ||
variations.forEach(variation => { | ||
const variationRouteDir = `${routeDir}_${variation.split(',')[0]}` | ||
routesDir[variationRouteDir] = routesDir[routeDir] | ||
}) | ||
}) | ||
}, | ||
} | ||
} | ||
|
||
const missingBaseComponentWarning = (node, newName, ctx) => { | ||
const variant = node.file.path | ||
const base = newName + node.file.ext | ||
ctx.tools.log.warn(`Node "${variant}" does not have a corresponding "${base}" node.`) | ||
} | ||
|
||
/** | ||
* | ||
* @param {Partial<FlexMapPluginOptions>} options | ||
* @returns {RoutifyBuildtimePlugin} | ||
*/ | ||
function flexMapsNormalize(options) { | ||
const allRootDirPreferences = createRootDirPreferences(options.variationsMap) | ||
|
||
return { | ||
name: 'flexMapPlugin-mergeVariantRoutes', | ||
after: 'metaFromFile', | ||
before: 'exporter', | ||
build: async ctx => { | ||
Object.entries(ctx.instance.rootNodes).forEach(([name, rootNode]) => { | ||
const rootDirPreferences = allRootDirPreferences[name] | ||
|
||
;[...(rootDirPreferences?.priorities || [])] | ||
.reverse() | ||
.forEach(priority => { | ||
rootNode.descendants.forEach(node => { | ||
// if node.name ends with the current rootNode language | ||
// remove the language from the name and remove the corresponding node | ||
|
||
if (node.name.endsWith(`.${priority}`)) { | ||
const newName = node.name.replace(`.${priority}`, '') | ||
|
||
// remove old node | ||
try { | ||
node.parent.traverse(`./${newName}`).remove() | ||
} catch (e) { | ||
if (e.message.match('could not travel to')) | ||
missingBaseComponentWarning(node, newName, ctx) | ||
} | ||
|
||
node.name = newName | ||
} | ||
}) | ||
}) | ||
// else if node.name ends with one of the other variations | ||
// remove the node | ||
rootNode.descendants.forEach(node => { | ||
if ( | ||
rootDirPreferences?.unwanted.some(variation => | ||
node.name.endsWith(`.${variation}`), | ||
) | ||
) { | ||
node.parent.traverse(`./${node.name}`).remove() | ||
} | ||
}) | ||
}) | ||
}, | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* @param {Object<string,string[]>} variationsMap | ||
*/ | ||
function createRootDirPreferences(variationsMap) { | ||
/** @type {Object<string, {priorities: string[], unwanted: string[]}>} */ | ||
const rootDirNameToPriorities = {} | ||
|
||
Object.entries(variationsMap).forEach(([_routeDir, variations]) => { | ||
const allVariations = variations.map(variation => variation.split(',')).flat() | ||
variations.forEach(variation => { | ||
const priorities = variation.split(',') | ||
const unwanted = allVariations.filter( | ||
variation => !priorities.includes(variation), | ||
) | ||
const routeDir = `${_routeDir}_${priorities[0]}` | ||
|
||
rootDirNameToPriorities[routeDir] = { priorities, unwanted } | ||
}) | ||
}) | ||
|
||
return rootDirNameToPriorities | ||
} |
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
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,120 @@ | ||
import { dirname } from 'path' | ||
import { fileURLToPath } from 'url' | ||
import { readFile } from 'fs/promises' | ||
import { flexMapsPlugin } from '../index.js' | ||
import { filemapper } from '../../../lib/buildtime/plugins/filemapper/lib/index.js' | ||
import { RoutifyBuildtime } from '../../../lib/buildtime/RoutifyBuildtime.js' | ||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url)) | ||
|
||
const nameAndPath = node => `${node.name} (${node.file.path})` | ||
|
||
const options = { | ||
routifyDir: `${__dirname}/temp`, | ||
routesDir: { | ||
default: `${__dirname}/example`, | ||
basicOnly: `${__dirname}/example`, | ||
withPriorities: `${__dirname}/example`, | ||
}, | ||
} | ||
|
||
describe('flexMap plugin', async () => { | ||
const instance = new RoutifyBuildtime(options) | ||
|
||
const [createRouteDirs, mergeVariantRoutes] = flexMapsPlugin({ | ||
variationsMap: { | ||
basicOnly: ['en', 'en-us', 'de'], | ||
withPriorities: ['en-us,en', 'en', 'de'], | ||
}, | ||
}) | ||
|
||
describe('createRouteDirs', async () => { | ||
await createRouteDirs.build({ instance, tools: null }) | ||
await filemapper({ instance }) | ||
test('basic', () => { | ||
assert.ok(instance.options.routesDir['basicOnly_en']) | ||
assert.ok(instance.options.routesDir['basicOnly_de']) | ||
}) | ||
test('withPriorities', () => { | ||
assert.ok(instance.options.routesDir['withPriorities_en-us']) | ||
assert.ok(instance.options.routesDir['withPriorities_en']) | ||
assert.ok(instance.options.routesDir['withPriorities_de']) | ||
}) | ||
}) | ||
|
||
describe('mergeVariantRoutes', async () => { | ||
await mergeVariantRoutes.build({ instance, tools: null }) | ||
|
||
test('rootNodes', () => { | ||
assert.ok(instance.rootNodes.default) | ||
assert.ok(instance.rootNodes.basicOnly_en) | ||
assert.ok(instance.rootNodes.basicOnly_de) | ||
|
||
assert.ok(instance.rootNodes.withPriorities_en) | ||
assert.ok(instance.rootNodes['withPriorities_en-us']) | ||
assert.ok(instance.rootNodes.withPriorities_de) | ||
}) | ||
|
||
test('default', () => { | ||
assert.deepEqual(instance.rootNodes.default.children.map(nameAndPath), [ | ||
'about.de (plugins/flexMap/spec/example/about.de.svelte)', | ||
'about.en (plugins/flexMap/spec/example/about.en.svelte)', | ||
'about (plugins/flexMap/spec/example/about.svelte)', | ||
'index.de (plugins/flexMap/spec/example/index.de.svelte)', | ||
'index.en-us (plugins/flexMap/spec/example/index.en-us.svelte)', | ||
'index.en (plugins/flexMap/spec/example/index.en.svelte)', | ||
'index (plugins/flexMap/spec/example/index.svelte)', | ||
'[...404] (plugins/flexMap/spec/temp/components/[...404].svelte)', | ||
]) | ||
}) | ||
|
||
test('basic_en', () => { | ||
assert.deepEqual(instance.rootNodes.basicOnly_en.children.map(nameAndPath), [ | ||
'about (plugins/flexMap/spec/example/about.en.svelte)', | ||
'index (plugins/flexMap/spec/example/index.en.svelte)', | ||
'[...404] (plugins/flexMap/spec/temp/components/[...404].svelte)', | ||
]) | ||
}) | ||
|
||
test('basic_de', () => { | ||
assert.deepEqual(instance.rootNodes.basicOnly_de.children.map(nameAndPath), [ | ||
'about (plugins/flexMap/spec/example/about.de.svelte)', | ||
'index (plugins/flexMap/spec/example/index.de.svelte)', | ||
'[...404] (plugins/flexMap/spec/temp/components/[...404].svelte)', | ||
]) | ||
}) | ||
|
||
test('withPriorities_en', () => { | ||
assert.deepEqual( | ||
instance.rootNodes.withPriorities_en.children.map(nameAndPath), | ||
[ | ||
'about (plugins/flexMap/spec/example/about.en.svelte)', | ||
'index (plugins/flexMap/spec/example/index.en.svelte)', | ||
'[...404] (plugins/flexMap/spec/temp/components/[...404].svelte)', | ||
], | ||
) | ||
}) | ||
|
||
test('withPriorities_en-us', () => { | ||
assert.deepEqual( | ||
instance.rootNodes['withPriorities_en-us'].children.map(nameAndPath), | ||
[ | ||
'about (plugins/flexMap/spec/example/about.en.svelte)', | ||
'index (plugins/flexMap/spec/example/index.en-us.svelte)', | ||
'[...404] (plugins/flexMap/spec/temp/components/[...404].svelte)', | ||
], | ||
) | ||
}) | ||
|
||
test('withPriorities_de', () => { | ||
assert.deepEqual( | ||
instance.rootNodes.withPriorities_de.children.map(nameAndPath), | ||
[ | ||
'about (plugins/flexMap/spec/example/about.de.svelte)', | ||
'index (plugins/flexMap/spec/example/index.de.svelte)', | ||
'[...404] (plugins/flexMap/spec/temp/components/[...404].svelte)', | ||
], | ||
) | ||
}) | ||
}) | ||
}) |
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { i18nPlugin } from './i18n/index.js' | ||
import { flexMapsPlugin } from './flexMap/index.js' | ||
import indexByNamePlugin from './indexByName.js' | ||
|
||
export { i18nPlugin, indexByNamePlugin } | ||
export { flexMapsPlugin, indexByNamePlugin } |