From 7f688ed89988a58eeb96e47bdc12acefbe1482c5 Mon Sep 17 00:00:00 2001 From: Jakob Rosenberg Date: Tue, 26 Dec 2023 17:54:43 +0100 Subject: [PATCH] feat: _inlineWrapper.svelte - a non recursive decorator that applies only to inlined nodes --- .../filemapper/lib/utils/filenameToOptions.js | 10 +++++ lib/runtime/Route/Route.js | 6 ++- lib/runtime/renderer/ComposeFragments.svelte | 26 ++++++----- lib/runtime/renderer/composeFragments.js | 45 ++++++++++--------- .../renderer/utils/normalizeDecorator.js | 14 +++++- .../__snapshots__/routify.test.js.snap | 3 +- types/typedef.js | 1 + typings/lib/runtime/Global/Global.d.ts | 1 + .../runtime/renderer/composeFragments.d.ts | 3 +- .../renderer/utils/normalizeDecorator.d.ts | 1 + typings/plugins/index.d.ts | 4 +- typings/plugins/indexByPrefix/index.d.ts | 8 ++++ typings/types/typedef.d.ts | 1 + 13 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 typings/plugins/indexByPrefix/index.d.ts diff --git a/lib/buildtime/plugins/filemapper/lib/utils/filenameToOptions.js b/lib/buildtime/plugins/filemapper/lib/utils/filenameToOptions.js index 2971e2bd..ba76312f 100644 --- a/lib/buildtime/plugins/filemapper/lib/utils/filenameToOptions.js +++ b/lib/buildtime/plugins/filemapper/lib/utils/filenameToOptions.js @@ -54,4 +54,14 @@ export const filenameToOptions = node => { .forEach(node => { node.meta.noRoute = true }) + descendants + .filter(node => node.name === '_decorator') + .forEach(node => { + node.meta.isDecorator = true + }) + descendants + .filter(node => node.name === '_inlineWrapper') + .forEach(node => { + node.meta.isInlineWrapper = true + }) } diff --git a/lib/runtime/Route/Route.js b/lib/runtime/Route/Route.js index 7132be6b..494896de 100644 --- a/lib/runtime/Route/Route.js +++ b/lib/runtime/Route/Route.js @@ -106,7 +106,11 @@ export class Route { this.log.debug('load components', this) // ROUTIFY-DEV-ONLY const nodes = this.fragments.map(fragment => fragment.node) const multiNodes = nodes - .map(node => node.children.find(node => node.name === '_decorator')) + .map(node => + node.children.find( + node => node.meta.isDecorator || node.meta.isInlineWrapper, + ), + ) .filter(Boolean) await Promise.all([...nodes, ...multiNodes].map(node => node.loadModule())) diff --git a/lib/runtime/renderer/ComposeFragments.svelte b/lib/runtime/renderer/ComposeFragments.svelte index c9a63e43..e9fae533 100644 --- a/lib/runtime/renderer/ComposeFragments.svelte +++ b/lib/runtime/renderer/ComposeFragments.svelte @@ -2,9 +2,13 @@ import { get } from 'svelte/store' import { pushToOrReplace } from '../utils/index.js' import RenderFragment from './RenderFragment.svelte' - import { normalizeDecorator } from './utils/normalizeDecorator.js' + import { normalizeDecorator, normalizeWrapper } from './utils/normalizeDecorator.js' import { handleRebuildError } from '../utils/messages.js' - import { addFolderDecorator, findActiveChildContext } from './composeFragments.js' + import { + addFolderDecorator, + addFolderWrapper, + findActiveChildContext, + } from './composeFragments.js' /** @type {RenderContext|RouterContext}*/ export let context @@ -14,16 +18,16 @@ const { childFragments } = context const { decorator } = options + const inlineWrapper = + options.inline?.['wrapper'] && normalizeWrapper(options.inline['wrapper']) const recursiveDecorators = context.decorators.filter(deco => deco.recursive) - const newDecorators = pushToOrReplace(recursiveDecorators, decorator) + const newDecorators = pushToOrReplace(recursiveDecorators, [decorator, inlineWrapper]) .filter(Boolean) .map(normalizeDecorator) - // addFolderDecorator returns void if decorator is sync, otherwise it returns a promise - let decoratorReady = !addFolderDecorator(newDecorators, context)?.['then']( - () => (decoratorReady = true), - ) + addFolderDecorator(newDecorators, context) + addFolderWrapper(newDecorators, context) context.buildChildContexts(options, newDecorators) @@ -64,8 +68,6 @@ $: _handleChildren($childFragments) -{#if decoratorReady} - {#each $childContexts as context (context)} - - {/each} -{/if} +{#each $childContexts as context (context)} + +{/each} diff --git a/lib/runtime/renderer/composeFragments.js b/lib/runtime/renderer/composeFragments.js index 81f7d1f8..128d0180 100644 --- a/lib/runtime/renderer/composeFragments.js +++ b/lib/runtime/renderer/composeFragments.js @@ -1,6 +1,7 @@ import { get } from 'svelte/store' import { RouteFragment } from '../Route/RouteFragment.js' import { RenderContext } from './RenderContext.js' +import { normalizeWrapper } from './utils/normalizeDecorator.js' /** * check if fragments have the same node and all a params are in b. @@ -22,36 +23,40 @@ export const nodeIsIndexed = node => export const fetchIndexNode = node => node.navigableChildren.find(node => node.meta.isDefault) -/** - * @param {RNodeRuntime} node - */ -const findDecorator = node => node?.children.find(node => node.name === '_decorator') - // TODO addFolderDecorator should always be synchronous /** * * @param {Decorator[]} decorators - * @param {RenderContext} context + * @param {RenderContext | RouterContext} context * @returns {void | Promise} */ export const addFolderDecorator = (decorators, context) => { - const folderDecorator = findDecorator(context.node) - - if (!folderDecorator) return + const folderDecorator = context['node']?.children.find(node => node.meta.isDecorator) - if (!folderDecorator.module) - return folderDecorator.loadModule().then(() => { - console.warn(`Dynamic import of "${folderDecorator.id}" may cause issues.`) - addFolderDecorator(decorators, context) + if (folderDecorator) { + const options = folderDecorator.module['decorator'] || {} + decorators.push({ + component: folderDecorator.module['default'], + recursive: options.recursive ?? folderDecorator.meta.recursive ?? true, + shouldRender: + options.shouldRender ?? folderDecorator.meta.shouldRender ?? (() => true), }) + } +} +/** + * + * @param {Decorator[]} decorators + * @param {RenderContext | RouterContext} context + * @returns {void | Promise} + */ +export const addFolderWrapper = (decorators, context) => { + const inlineWrapper = context['node']?.children.find( + node => node.meta.isInlineWrapper, + ) - const options = folderDecorator.module['decorator'] || {} - - decorators.push({ - component: folderDecorator.module['default'], - recursive: options.recursive ?? folderDecorator.meta.recursive ?? true, - shouldRender: options.shouldRender ?? (() => true), - }) + if (inlineWrapper) { + decorators.push(normalizeWrapper(inlineWrapper.module.default)) + } } export function findNearestInlineContext(context) { diff --git a/lib/runtime/renderer/utils/normalizeDecorator.js b/lib/runtime/renderer/utils/normalizeDecorator.js index 4c848a23..49bdd18f 100644 --- a/lib/runtime/renderer/utils/normalizeDecorator.js +++ b/lib/runtime/renderer/utils/normalizeDecorator.js @@ -1,4 +1,11 @@ -const decoratorDefaults = { recursive: true, shouldRender: () => true } +const decoratorDefaults = { + recursive: true, + shouldRender: () => true, +} +const wrapperDefaults = { + recursive: false, + shouldRender: ({ context }) => context.isInline, +} /** * @param {DecoratorInput} decorator @@ -9,4 +16,9 @@ export const normalizeDecorator = decorator => { else return { ...decoratorDefaults, component: decorator } } +export const normalizeWrapper = wrapper => { + if ('component' in wrapper) return { ...wrapperDefaults, ...wrapper } + else return { ...wrapperDefaults, component: wrapper } +} + export * from './normalizeInline.js' diff --git a/test/integration/routify/__snapshots__/routify.test.js.snap b/test/integration/routify/__snapshots__/routify.test.js.snap index 124fbacb..15f69196 100644 --- a/test/integration/routify/__snapshots__/routify.test.js.snap +++ b/test/integration/routify/__snapshots__/routify.test.js.snap @@ -184,7 +184,8 @@ export default { \\"meta\\": { \\"dynamic\\": true, \\"dynamicSpread\\": true, - \\"order\\": false + \\"order\\": false, + \\"inline\\": false }, \\"name\\": \\"[...404]\\", \\"file\\": { diff --git a/types/typedef.js b/types/typedef.js index 1b8096ad..f6d01631 100644 --- a/types/typedef.js +++ b/types/typedef.js @@ -371,6 +371,7 @@ * @prop { 'browser'|'ssr'|'always' } context * @prop { (elem: HTMLElement, instant: boolean) => void } scrollIntoView * @prop { Object } params + * @prop { SvelteComponentDev? } wrapper */ /** diff --git a/typings/lib/runtime/Global/Global.d.ts b/typings/lib/runtime/Global/Global.d.ts index 40554947..2eb8e7bc 100644 --- a/typings/lib/runtime/Global/Global.d.ts +++ b/typings/lib/runtime/Global/Global.d.ts @@ -9,6 +9,7 @@ export class AppInstance { /** @type {import('../helpers/preload.js').RoutesMap} */ routeMaps: import('../helpers/preload.js').RoutesMap; browserAdapter: BrowserAdapter; + reset(): void; /** @param {Router} router */ urlFromBrowser: (router: Router) => string; register(instance: any): AppInstance; diff --git a/typings/lib/runtime/renderer/composeFragments.d.ts b/typings/lib/runtime/renderer/composeFragments.d.ts index 7dea016a..71f69ece 100644 --- a/typings/lib/runtime/renderer/composeFragments.d.ts +++ b/typings/lib/runtime/renderer/composeFragments.d.ts @@ -2,7 +2,8 @@ export function findNearestInlineContext(context: any): any; export function contextHasMatchingFragmentAndParams(f: RouteFragment): (c: RenderContext) => boolean; export function nodeIsIndexed(node: RNodeRuntime): boolean; export function fetchIndexNode(node: RNodeRuntime): import("../Instance/RNodeRuntime.js").RNodeRuntime; -export function addFolderDecorator(decorators: Decorator[], context: RenderContext): void | Promise; +export function addFolderDecorator(decorators: Decorator[], context: RenderContext | RouterContext): void | Promise; +export function addFolderWrapper(decorators: Decorator[], context: RenderContext | RouterContext): void | Promise; export function defaultscrollBoundary(ownContext: any): any; export function findActiveChildContext(childContexts: RenderContext[], fragment: RouteFragment): RenderContext; import { RouteFragment } from "../Route/RouteFragment.js"; diff --git a/typings/lib/runtime/renderer/utils/normalizeDecorator.d.ts b/typings/lib/runtime/renderer/utils/normalizeDecorator.d.ts index 4ec270e4..8e3a7d55 100644 --- a/typings/lib/runtime/renderer/utils/normalizeDecorator.d.ts +++ b/typings/lib/runtime/renderer/utils/normalizeDecorator.d.ts @@ -1,2 +1,3 @@ export function normalizeDecorator(decorator: DecoratorInput): Decorator; +export function normalizeWrapper(wrapper: any): any; export * from "./normalizeInline.js"; diff --git a/typings/plugins/index.d.ts b/typings/plugins/index.d.ts index c2000377..f722539b 100644 --- a/typings/plugins/index.d.ts +++ b/typings/plugins/index.d.ts @@ -1,3 +1,3 @@ import { flexMapsPlugin } from "./flexMap/index.js"; -import indexByNamePlugin from "./indexByName.js"; -export { flexMapsPlugin, indexByNamePlugin }; +import indexByPrefixPlugin from "./indexByPrefix/index.js"; +export { flexMapsPlugin, indexByPrefixPlugin }; diff --git a/typings/plugins/indexByPrefix/index.d.ts b/typings/plugins/indexByPrefix/index.d.ts new file mode 100644 index 00000000..f432f420 --- /dev/null +++ b/typings/plugins/indexByPrefix/index.d.ts @@ -0,0 +1,8 @@ +declare function _default(options: any): { + name: string; + before: string; + build: ({ instance }: { + instance: any; + }) => void; +}; +export default _default; diff --git a/typings/types/typedef.d.ts b/typings/types/typedef.d.ts index 13796495..b3a4fd55 100644 --- a/typings/types/typedef.d.ts +++ b/typings/types/typedef.d.ts @@ -445,6 +445,7 @@ type Inline = { params: { [x: string]: string[]; }; + wrapper: SvelteComponentDev | null; }; type DeferredPromise = Promise & { resolve: (T: any) => void;