Skip to content

Commit

Permalink
Merge branch 'main' into refactor/generator-cli/file-read
Browse files Browse the repository at this point in the history
  • Loading branch information
Lodin authored Feb 20, 2025
2 parents bfac16d + f84a879 commit b796312
Show file tree
Hide file tree
Showing 8 changed files with 548 additions and 531 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ignorePatterns": ["packages/ts/*/test/**/*.snap.ts"],
"plugins": ["mocha", "tsdoc"],
"rules": {
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/no-useless-template-literals": "off",
"@typescript-eslint/no-invalid-void-type": "off",
"@typescript-eslint/member-ordering": "off",
Expand Down
863 changes: 424 additions & 439 deletions package-lock.json

Large diffs are not rendered by default.

42 changes: 21 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
"overrides": {
"esbuild": "$esbuild",
"prettier": "$prettier",
"vite": "$vite"
"vite": "6.1.1"
},
"devDependencies": {
"@nx/js": "20.4.0",
"@nx/js": "20.4.5",
"@open-wc/testing": "4.0.0",
"@preact/signals-react-transform": "0.5.1",
"@testing-library/dom": "10.4.0",
Expand All @@ -47,20 +47,20 @@
"@types/chai-like": "1.1.3",
"@types/deep-equal-in-any-order": "1.0.4",
"@types/js-cookie": "3.0.6",
"@types/node": "22.12.0",
"@types/react": "19.0.8",
"@types/node": "22.13.4",
"@types/react": "19.0.10",
"@types/react-dom": "19",
"@types/sinon": "17.0.3",
"@types/sinon-chai": "4.0.0",
"@types/validator": "13.12.2",
"@vaadin/react-components": "24.7.0-alpha9",
"@vitejs/plugin-react": "4.3.4",
"@vitest/browser": "3.0.4",
"@vitest/coverage-v8": "3.0.4",
"@vitest/ui": "3.0.4",
"@web/test-runner": "0.19.0",
"@vitest/browser": "3.0.6",
"@vitest/coverage-v8": "3.0.6",
"@vitest/ui": "3.0.6",
"@web/test-runner": "0.20.0",
"c8": "10.1.3",
"chai": "5.1.2",
"chai": "5.2.0",
"chai-as-promised": "8.0.1",
"chai-dom": "1.12.1",
"chai-like": "1.1.3",
Expand All @@ -69,34 +69,34 @@
"copyfiles": "2.4.1",
"cssnano": "7.0.6",
"deep-equal-in-any-order": "2.0.6",
"esbuild": "0.24.2",
"esbuild": "0.25.0",
"eslint": "8.57.0",
"eslint-config-vaadin": "1.0.0-alpha.28",
"eslint-plugin-mocha": "10.5.0",
"eslint-plugin-prettier": "5.2.3",
"eslint-plugin-tsdoc": "0.4.0",
"fetch-mock": "12.2.1",
"fetch-mock": "12.3.0",
"glob": "11.0.1",
"lint-staged": "15.4.3",
"magic-string": "0.30.17",
"micromatch": "4.0.8",
"nx": "20.4.0",
"nx": "20.4.5",
"pino": "9.6.0",
"playwright": "1.50.0",
"postcss": "8.5.1",
"prettier": "3.4.2",
"playwright": "1.50.1",
"postcss": "8.5.2",
"prettier": "3.5.1",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-router": "7.1.3",
"rollup": "4.32.1",
"react-router": "7.2.0",
"rollup": "4.34.8",
"simple-git-hooks": "2.11.1",
"sinon": "19.0.2",
"sinon-chai": "4.0.0",
"sync-request": "6.1.0",
"tsx": "4.19.2",
"type-fest": "4.33.0",
"tsx": "4.19.3",
"type-fest": "4.35.0",
"typescript": "5.7.3",
"vite": "6.0.11",
"vitest": "3.0.4"
"vite": "6.1.1",
"vitest": "3.0.6"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,13 @@
package com.vaadin.hilla.route;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

import com.vaadin.flow.function.DeploymentConfiguration;
import com.vaadin.flow.router.RouteConfiguration;
import com.vaadin.flow.server.VaadinRequest;
import com.vaadin.flow.server.auth.MenuAccessControl;
import com.vaadin.flow.server.auth.NavigationAccessControl;
import com.vaadin.flow.server.auth.ViewAccessChecker;
import com.vaadin.flow.server.menu.AvailableViewInfo;
import com.vaadin.flow.internal.menu.MenuRegistry;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;

import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.jsoup.nodes.DataNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.communication.IndexHtmlRequestListener;
import com.vaadin.flow.server.communication.IndexHtmlResponse;
import com.vaadin.flow.server.menu.RouteParamType;

/**
* Index HTML request listener for collecting the client side and the server
Expand Down
20 changes: 14 additions & 6 deletions packages/ts/file-router/src/runtime/createMenuItems.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
/// <reference types="vite/client" />
import { signal } from '@vaadin/hilla-react-signals';
import type { VaadinWindow } from '../shared/internal.js';
import type { MenuItem } from '../types.js';
import type { MenuItem, ViewConfig } from '../types.js';

export const viewsSignal = signal((window as VaadinWindow).Vaadin?.views);

function isExcluded(value: ViewConfig): boolean {
return !!value.menu?.exclude;
}

function hasVariablePathSegment(path: string): boolean {
return path.split('/').some((segment) => segment.startsWith(':'));
}

/**
* Creates menu items from the views provided by the server. The views are sorted according to the
* {@link ViewConfig.menu.order}, filtered out if they are explicitly excluded via {@link ViewConfig.menu.exclude}.
Expand All @@ -21,10 +30,12 @@ export function createMenuItems(): readonly MenuItem[] {
return [];
}

const views = Object.entries(viewsSignal.value);

return (
Object.entries(viewsSignal.value)
views
// Filter out the views that are explicitly excluded from the menu.
.filter(([_, value]) => !value.menu?.exclude)
.filter(([path, value]) => !isExcluded(value) && !hasVariablePathSegment(path))
// Map the views to menu items.
.map(([path, config]) => ({
to: path,
Expand All @@ -40,10 +51,7 @@ export function createMenuItems(): readonly MenuItem[] {
);
}

// @ts-expect-error Vite hotswapping
if (import.meta.hot) {
// @ts-expect-error Vite hotswapping
// eslint-disable-next-line
import.meta.hot.on('fs-route-update', () => {
fetch('?v-r=routeinfo')
.then(async (resp) => resp.json())
Expand Down
109 changes: 74 additions & 35 deletions packages/ts/file-router/test/runtime/createMenuItems.spec.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,10 @@
import './vaadinGlobals.js'; // eslint-disable-line import/no-unassigned-import
import chaiLike from 'chai-like';
import type { Writable } from 'type-fest';
import { expect, chai, describe, it } from 'vitest';
import { expect, describe, it } from 'vitest';
import { createMenuItems, viewsSignal } from '../../src/runtime/createMenuItems.js';
import type { MenuItem } from '../../src/types.js';

chai.use(chaiLike);
import { deepRemoveNullProps } from '../utils.js';

const collator = new Intl.Collator('en-US');

function cleanup(items: Array<Writable<MenuItem>>) {
items
.sort(({ to: a }, { to: b }) => collator.compare(a, b))
.forEach((item) => {
if (!item.title) {
delete item.title;
}

if (!item.to) {
delete item.icon;
}
});
}

describe('@vaadin/hilla-file-router', () => {
describe('createMenuItems', () => {
it('should generate a set of menu items', () => {
Expand All @@ -35,10 +17,11 @@ describe('@vaadin/hilla-file-router', () => {
'/test/empty': {},
'/test/no-default-export': { title: 'No Default Export' },
};
const items = createMenuItems();
cleanup(items as Array<Writable<MenuItem>>);
const items = createMenuItems()
.slice()
.sort(({ to: a }, { to: b }) => collator.compare(a, b));

const expected = [
expect(deepRemoveNullProps(items)).to.be.deep.equal([
{
title: 'About',
to: '/about',
Expand Down Expand Up @@ -66,10 +49,7 @@ describe('@vaadin/hilla-file-router', () => {
title: 'No Default Export',
to: '/test/no-default-export',
},
];
cleanup(expected);

expect(items).to.be.like(expected);
]);
});

it('should sort menu items by order then by natural string comparison based on path', () => {
Expand All @@ -83,26 +63,25 @@ describe('@vaadin/hilla-file-router', () => {
'/test/empty': { title: 'empty', menu: { order: 5 } },
'/test/no-default-export': { title: 'No Default Export', menu: { order: 10 } },
};
const items = createMenuItems();
const cleanedUp = (items as Array<Writable<MenuItem>>).map((item) => ({
to: item.to,
title: item.title,
}));

const expected = [
expect(deepRemoveNullProps(createMenuItems())).to.be.deep.equal([
{
order: 5,
title: 'empty',
to: '/test/empty',
},
{
order: 10,
title: 'No Default Export',
to: '/test/no-default-export',
},
{
order: 20,
title: 'Password',
to: '/profile/account/security/password',
},
{
order: 20,
title: 'Two Factor Auth',
to: '/profile/account/security/two-factor-auth',
},
Expand All @@ -122,9 +101,69 @@ describe('@vaadin/hilla-file-router', () => {
title: 'List',
to: '/b',
},
];
]);
});

it('should exclude items with { "menu": { "exclude": true } } but leave children in place', () => {
viewsSignal.value = {
'/foo': { title: 'Foo' },
'/bar': { title: 'Bar', menu: { exclude: true } }, // should be excluded
'/bar/foo': { title: 'Bar Foo' },
'/baz': { title: 'Baz' },
'/baz/bar': { title: 'Baz Bar' },
'/baz/bar/foo': { title: 'Baz Bar Foo', menu: { exclude: true } }, // should be excluded
'/baz/bar/foo/buzz': { title: 'Baz Bar Foo Buzz' },
};

expect(deepRemoveNullProps(createMenuItems())).to.be.deep.equal([
{
title: 'Bar Foo',
to: '/bar/foo',
},
{
title: 'Baz',
to: '/baz',
},
{
title: 'Baz Bar',
to: '/baz/bar',
},
{
title: 'Baz Bar Foo Buzz',
to: '/baz/bar/foo/buzz',
},
{
title: 'Foo',
to: '/foo',
},
]);
});

expect(cleanedUp).to.deep.equal(expected);
it('should exclude items with variable path segments and their children', () => {
viewsSignal.value = {
'/foo': { title: 'Foo' },
'/bar/:id': { title: 'Bar' }, // should be excluded
'/bar/:id/foo': { title: 'Bar Foo' }, // should be excluded
'/baz': { title: 'Baz' },
'/baz/foo': { title: 'Baz Foo' },
'/baz/bar/:id': { title: 'Baz Bar' }, // should be excluded
'/baz/bar/:id/foo': { title: 'Baz Bar Foo' }, // should be excluded
};

expect(deepRemoveNullProps(createMenuItems())).to.be.deep.equal([
{
title: 'Baz',
to: '/baz',
},
{
title: 'Baz Foo',
to: '/baz/foo',
},
{
title: 'Foo',
to: '/foo',
},
]);
});
});
});
14 changes: 14 additions & 0 deletions packages/ts/file-router/test/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,17 @@ export function createLogger(): Logger {
hasWarned: false,
};
}

export function deepRemoveNullProps<T>(input: T): T {
if (typeof input !== 'object' || input == null) {
return input;
}

return Array.isArray(input)
? (input.filter((item) => item != null).map((item) => deepRemoveNullProps(item)) as T)
: (Object.fromEntries(
Object.entries(input)
.filter(([, value]) => value != null)
.map(([key, value]) => [key, deepRemoveNullProps(value)]),
) as T);
}
1 change: 0 additions & 1 deletion scripts/vite/browser.vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference,spaced-comment
/// <reference types="vitest/node" />
import react from '@vitejs/plugin-react';
import { mergeConfig, type ViteUserConfig } from 'vitest/config';
Expand Down

0 comments on commit b796312

Please sign in to comment.