Skip to content

Commit

Permalink
fix: include parameters from components that have been excluded with …
Browse files Browse the repository at this point in the history
…reset

closes #518
  • Loading branch information
jakobrosenberg committed Jul 5, 2023
1 parent 047c59d commit ec9a703
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 72 deletions.
5 changes: 4 additions & 1 deletion lib/runtime/Instance/Node.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<script>
import { getRoutifyFragmentContext, setRoutifyFragmentContext } from '../utils'
import {
getRoutifyFragmentContext,
setRoutifyFragmentContext,
} from '../utils/index.js'
export let node
export let passthrough
Expand Down
13 changes: 5 additions & 8 deletions lib/runtime/Route/Route.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ export class Route {
/** @type {RouteFragment[]} */
allFragments = []
/** @type {RouteFragment[]} only fragments with components */
get fragments() {
return this.router.transformFragments.run(this.allFragments)
}
fragments = []

/** @type {RoutifyLoadReturn} */
load = {
Expand Down Expand Up @@ -52,6 +50,8 @@ export class Route {
throw new Error('url.mode must be pushState, replaceState or popState')

this.allFragments = this._createFragments()
this.fragments = this.router.transformFragments.run(this.allFragments)

this.log = router.log.createChild('[route]') // ROUTIFY-DEV-ONLY
this.log.debug('created', this) // ROUTIFY-DEV-ONLY
}
Expand All @@ -68,7 +68,7 @@ export class Route {
}

get leaf() {
return [...this.allFragments].pop()
return [...this.fragments].pop()
}

get isPendingOrPrefetch() {
Expand Down Expand Up @@ -161,10 +161,7 @@ export class Route {
}

get meta() {
return this.allFragments.reduce(
(acc, curr) => ({ ...acc, ...curr.node.meta }),
{},
)
return this.fragments.reduce((acc, curr) => ({ ...acc, ...curr.node.meta }), {})
}

/**
Expand Down
4 changes: 0 additions & 4 deletions lib/runtime/Route/RouteFragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ export class RouteFragment {
**/
_params = {}

get index() {
return this.route.fragments.indexOf(this)
}

get params() {
return URIDecodeObject(this._params)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/runtime/Route/spec/Route.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const router = new Router({ routes: exported })
test('creates correct fragments for a route', () => {
const route = new Route(router, '/admin', 'pushState')

expect(route.fragments.map(f => f.node.name)).toEqual(['', 'admin'])
expect(route.fragments.map(f => f.node.name)).toEqual(['admin'])
})

test('creates correct fragments for a nested route', () => {
Expand Down
2 changes: 2 additions & 0 deletions lib/runtime/Router/Router.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
const context = {
childFragments: writable([]),
decorators: [normalizeDecorator(ScrollDecorator)],
route: null,
}
/** @type {RoutifyRuntimeOptions}*/
Expand Down Expand Up @@ -83,6 +84,7 @@
$: if (url && url !== router.url.internal()) router.url.replace(url)
$: activeRoute = router.activeRoute
$: context.childFragments.set($activeRoute?.fragments || [])
$: context.route = $activeRoute
$: router.log.debug('before render', get(context.childFragments)) // ROUTIFY-DEV-ONLY
Expand Down
109 changes: 55 additions & 54 deletions lib/runtime/plugins/reset/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,73 @@ const parseModuleName = str => {
return { name, prepend }
}

const handlers = {
/**
* @param {Route} route
* @param {Boolean} bool
* @param {RouteFragment} fragment
*/
boolean(route, bool, fragment) {
const index = fragment.index
return handlers.number(route, index, fragment)
},
/**
* @param {Route} route
* @param {Number} num
* @param {RouteFragment} fragment
*/
number(route, num, fragment) {
const index = fragment.index
const start = index - num
route.allFragments.splice(start, num)
},
/**
* @param {Route} route
* @param {string} str
* @param {RouteFragment} fragment
*/
string(route, str, fragment) {
const selfIndex = fragment.index
const precedingFragments = route.allFragments.slice(0, selfIndex + 1)
/** @type {RouteFragment} */
let nextFragment
const createHandlers = (fragments, route) => {
const getIndexOf = fragment => fragments.indexOf(fragment)
const handlers = {
/**
* @param {Boolean} _bool
* @param {RouteFragment} fragment
*/
boolean(_bool, fragment) {
const index = getIndexOf(fragment)
return handlers.number(index, fragment)
},
/**
* @param {Number} num
* @param {RouteFragment} fragment
*/
number(num, fragment) {
const index = fragments.indexOf(fragment)
const start = index - num
fragments.splice(start, num)
},
/**
* @param {string} str
* @param {RouteFragment} fragment
*/
string(str, fragment) {
const selfIndex = getIndexOf(fragment)
const precedingFragments = fragments.slice(0, selfIndex + 1)
/** @type {RouteFragment} */
let nextFragment

const { name, prepend } = parseModuleName(str)
while (precedingFragments.length) {
nextFragment = precedingFragments.pop()
const matchingSiblingNode = nextFragment.node.children.find(
node => node.meta.moduleName === name,
)
if (matchingSiblingNode) {
if (!prepend) route.allFragments.splice(0, fragment.index)
route.allFragments.unshift(route.createFragment(matchingSiblingNode))
precedingFragments.splice(0)
const { name, prepend } = parseModuleName(str)
while (precedingFragments.length) {
nextFragment = precedingFragments.pop()
const matchingSiblingNode = nextFragment.node.children.find(
node => node.meta.moduleName === name,
)
if (matchingSiblingNode) {
if (!prepend) fragments.splice(0, getIndexOf(fragment))
fragments.unshift(route.createFragment(matchingSiblingNode))
precedingFragments.splice(0)
}
}
}
},
},
}
return handlers
}

/**
*
* @param {Route} route
* @param {ReturnType<createHandlers>} handlers
* @returns {(fragment: RouteFragment) => void}
*/
const handleFragment = route => fragment => {
const handleFragment = handlers => fragment => {
const { reset } = fragment.node.meta
if (reset) handlers[typeof reset](route, reset, fragment)
if (reset) handlers[typeof reset](reset, fragment)
}

/**
* Removes parent modules from a node
* @returns {RoutifyRuntimePlugin}
**/
export default () => {
return {
beforeUrlChange: ({ route }) => {
const fragments = [...route.allFragments]
fragments.forEach(handleFragment(route))
return true
},
}
}
export default () => ({
transformFragments: _fragments => {
const { route } = _fragments[0]
const fragments = [..._fragments]
const handlers = createHandlers(fragments, route)
_fragments.forEach(handleFragment(handlers))
return fragments
},
})
8 changes: 8 additions & 0 deletions lib/runtime/renderer/ComposeFragments.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
coerceInlineInputToObject,
normalizeInline,
} from './utils/normalizeDecorator.js'
import { handleRebuildError } from '../utils/messages.js'
export const isRoot = undefined
/** @type {RenderContext}*/
Expand Down Expand Up @@ -130,6 +131,8 @@
// if parent changes status to inactive, so does children
$: if (!$isActive) childContexts.forEach(cc => cc.isActive.set(false))
let lastRebuildRoute = null
/**
* @param {RouteFragment[]} fragments
*/
Expand All @@ -141,6 +144,11 @@
// if we're rendering a node that didn't exist at this level before, we need to rebuild the child contexts
// this happens when navigating in or out of a reset module
childContexts = buildChildContexts()
if (lastRebuildRoute === context.route)
handleRebuildError(context, childContexts)
else lastRebuildRoute = context.route
return handlePageChange(fragments)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/runtime/renderer/RenderFragment.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import DecoratorWrapper from './DecoratorWrapper.svelte'
import Noop from '../decorators/Noop.svelte'
import AnchorDecorator from '../decorators/AnchorDecorator.svelte'
import { isAnonFn, setRoutifyFragmentContext, waitFor } from '../utils'
import { isAnonFn, setRoutifyFragmentContext, waitFor } from '../utils/index.js'
/** @type {RenderContext} */
export let context
export let props
Expand Down
9 changes: 9 additions & 0 deletions lib/runtime/utils/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ Eg. routesDir: {

export const noRoutesMapProvided = name =>
`No routesMap provided. Make sure you\'ve either created a router for the '${name}' route or provided a routesMap`

export const handleRebuildError = (context, childContexts) => {
console.warn('Failed to rebuild routes', { context, childContexts })
const msg =
'Infinite loop detected while trying to compose components. This is likely an error in Routify.'
const err = new Error(msg)

throw err
}
14 changes: 11 additions & 3 deletions test/integration/routify/routify.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,17 @@ test('can transform output files', async () => {

await emptyTransformInstance.start()

expect(ids[0]).toMatch(/_default_admin-bundle.js$/)
expect(ids[1]).toMatch(/routes.default.js$/)
expect(ids[2]).toMatch(/instance.default.js$/)
const filenames = ids.map(id => id.replace(/.+[\\/]/g, ''))
const expectedFilenames = [
'_default_admin-bundle.js',
'routes.default.js',
'render.js',
'routify-init.js',
'route-map.js',
'instance.default.js',
'sitemap.default.txt',
]
assert.deepEqual(filenames, expectedFilenames)

const emptyTransformExpectedContent = readFileSync(bundlePath, 'utf-8')
expect(emptyTransformExpectedContent).toEqual(baseExpectedContent)
Expand Down

0 comments on commit ec9a703

Please sign in to comment.