Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rewrite-routes-exclude.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@qwik.dev/router': minor
---

feat(router): add an `exclude` option to `rewriteRoutes` to skip generating localized routes for matching path patterns.
16 changes: 16 additions & 0 deletions packages/docs/src/routes/docs/(qwikrouter)/routing/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,22 @@ export default defineConfig(async () => {
});
```

By default a config rewrites every matching route. Add `exclude` to skip routes that should not be rewritten under that config. The patterns use the same glob syntax as the SSG `include`/`exclude` and are matched per config:

```tsx
rewriteRoutes: [
{
prefix: 'it',
paths: {
'docs': 'documentazione',
'products': 'prodotti',
},
// `/blog` stays English-only: no `/it` mirror is generated for it.
exclude: ['/blog/*'],
},
],
```

## Advanced routing

Qwik Router also supports:
Expand Down
11 changes: 11 additions & 0 deletions packages/qwik-router/src/buildtime/build-pages-rewrited.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,14 @@ testSameRoutes(
assert.equal(r.pathname, '/produkt/');
}
);

const testExclude = testAppSuite('Rewrite routes with exclude', {
rewriteRoutes: [{ prefix: 'it', paths: {}, exclude: ['/docs/*'] }],
});

testExclude('exclude skips localized mirrors for matched routes', ({ ctx }) => {
const it = ctx.routeTrie.children.get('it');
assert.ok(it, 'prefix mirror "it" should exist for non-excluded routes');
assert.ok(it!.children.get('about-us'), 'non-excluded route is mirrored under "it"');
assert.is(it!.children.get('docs'), undefined, '"/docs/*" is excluded from the "it" mirror');
});
11 changes: 8 additions & 3 deletions packages/qwik-router/src/buildtime/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { addError, addWarning } from '../utils/format';
import { createFileId, getPathnameFromDirPath } from '../utils/fs';
import { ensureSlash } from '../utils/pathname';
import { createRouteTester } from '../ssg/routes';
import { resolveMenu } from './markdown/menu';
import { resolveLayout, resolveRoute } from './routing/resolve-source-file';
import { routeSortCompare } from './routing/sort-routes';
Expand Down Expand Up @@ -323,8 +324,15 @@ function applyRewriteRoutes(root: BuildTrieNode, rewriteConfigs: RewriteRouteOpt
for (const config of rewriteConfigs) {
const translations = config.paths || {};
const translatable = new Set(Object.keys(translations).map((k) => k.toLowerCase()));
const isExcluded = config.exclude?.length
? createRouteTester('/', config.exclude, undefined)
: undefined;

for (const { steps } of routables) {
const originalKeyPath = steps.map((s) => s.key).join('/');
if (isExcluded?.('/' + originalKeyPath)) {
continue;
}
// Check if any segment is translatable or if there's a prefix
const hasTranslatable = steps.some((s) => translatable.has(s.key));
if (!hasTranslatable && !config.prefix) {
Expand All @@ -335,9 +343,6 @@ function applyRewriteRoutes(root: BuildTrieNode, rewriteConfigs: RewriteRouteOpt
continue;
}

// Build the original key path
const originalKeyPath = steps.map((s) => s.key).join('/');

// Build translated steps
const translatedSteps: TriePathStep[] = [];

Expand Down
1 change: 1 addition & 0 deletions packages/qwik-router/src/buildtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export interface ParsedMenuItem {
export interface RewriteRouteOption {
prefix?: string;
paths: Record<string, string>;
exclude?: string[];
}

/** @public */
Expand Down