Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: rename views.ts to file-routes.ts #2271

Merged
merged 9 commits into from
Apr 3, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
@Component
public class ClientRouteRegistry implements Serializable {

public static final String FILE_ROUTES_JSON_NAME = "file-routes.json";
public static final String FILE_ROUTES_JSON_PROD_PATH = "/META-INF/VAADIN/"
+ FILE_ROUTES_JSON_NAME;

/**
* A map of registered routes and their corresponding client view
* configurations with ordered insertion.
Expand Down Expand Up @@ -121,51 +125,59 @@ private String removeTrailingSlash(String path) {
}

/**
* Registers client routes from views.json file generated by the
* file-router's Vite plugin. The views.json file is expected to be in the
* frontend/generated folder in dev mode and in the META-INF/VAADIN folder
* in production mode.
* Registers client routes from file-routes.json file generated by the
* file-router's Vite plugin. The file-routes.json file is expected to be in
* the frontend/generated folder in dev mode and in the META-INF/VAADIN
* folder in production mode.
*
* @param deploymentConfiguration
* the deployment configuration
*
* @return {@code true} if the client routes were successfully registered,
* {@code false} otherwise
*/
public void registerClientRoutes(
public boolean registerClientRoutes(
DeploymentConfiguration deploymentConfiguration) {
var viewsJsonAsResource = getViewsJsonAsResource(
deploymentConfiguration);
if (viewsJsonAsResource == null || !Paths
.get(viewsJsonAsResource.getPath()).toFile().exists()) {
if (viewsJsonAsResource == null) {
LOGGER.debug(
"No 'views.json' found either in the frontend/generated "
+ "folder or in the META-INF/VAADIN folder. Skipping client "
+ "route registration.");
return;
"No {} found under {} directory. Skipping client route registration.",
FILE_ROUTES_JSON_NAME,
deploymentConfiguration.isProductionMode()
? "'META-INF/VAADIN'"
: "'frontend/generated'");
return false;
}
try (var source = viewsJsonAsResource.openStream()) {
if (source != null) {
clearRoutes();
registerAndRecurseChildren("",
mapper.readValue(source, new TypeReference<>() {
}));
return true;
}
return false;
} catch (IOException e) {
LOGGER.warn("Failed load client views from {}",
LOGGER.warn("Failed load {} from {}", FILE_ROUTES_JSON_NAME,
viewsJsonAsResource.getPath(), e);
return false;
}
}

private URL getViewsJsonAsResource(
DeploymentConfiguration deploymentConfiguration) {
var isProductionMode = deploymentConfiguration.isProductionMode();
if (isProductionMode) {
return getClass().getResource("/META-INF/VAADIN/views.json");
return getClass().getResource(FILE_ROUTES_JSON_PROD_PATH);
}
try {
return deploymentConfiguration.getFrontendFolder().toPath()
.resolve("generated").resolve("views.json").toUri().toURL();
.resolve("generated").resolve(FILE_ROUTES_JSON_NAME).toUri()
.toURL();
} catch (MalformedURLException e) {
LOGGER.warn("Failed to find views.json under frontend/generated",
e);
LOGGER.warn("Failed to find {} under frontend/generated",
FILE_ROUTES_JSON_NAME, e);
throw new RuntimeException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,27 @@ public void modifyIndexHtmlResponse(IndexHtmlResponse response) {
collectServerViews(availableViews);

if (availableViews.isEmpty()) {
LOGGER.debug(
"No server-side nor client-side views found, skipping response modification.");
return;
}
try {
final String viewsJson = mapper.writeValueAsString(availableViews);
final String script = SCRIPT_STRING.formatted(viewsJson);
final String fileRoutesJson = mapper
.writeValueAsString(availableViews);
final String script = SCRIPT_STRING.formatted(fileRoutesJson);
response.getDocument().head().appendElement("script")
.appendChild(new DataNode(script));
} catch (IOException e) {
LOGGER.error("Failed to write server views to index response", e);
LOGGER.error(
"Failure while to write client and server routes to index html response",
e);
}
}

protected void collectClientViews(
Map<String, AvailableViewInfo> availableViews) {
if (!deploymentConfiguration.isProductionMode()) {
loadLatestDevModeViewsJsonIfNeeded();
loadLatestDevModeFileRoutesJsonIfNeeded();
} else if (lastUpdated == null) {
// initial (and only) registration in production mode:
registerClientRoutes(LocalDateTime.now());
Expand All @@ -109,16 +114,17 @@ protected void collectClientViews(

}

private void loadLatestDevModeViewsJsonIfNeeded() {
var devModeViewsJsonFile = deploymentConfiguration.getFrontendFolder()
.toPath().resolve("generated").resolve("views.json").toFile();
if (!devModeViewsJsonFile.exists()) {
LOGGER.warn("Failed to find views.json under {}",
private void loadLatestDevModeFileRoutesJsonIfNeeded() {
var devModeFileRoutesJsonFile = deploymentConfiguration
.getFrontendFolder().toPath().resolve("generated")
.resolve("file-routes.json").toFile();
if (!devModeFileRoutesJsonFile.exists()) {
LOGGER.debug("No file-routes.json found under {}",
deploymentConfiguration.getFrontendFolder().toPath()
.resolve("generated"));
return;
}
var lastModified = devModeViewsJsonFile.lastModified();
var lastModified = devModeFileRoutesJsonFile.lastModified();
var lastModifiedTime = Instant.ofEpochMilli(lastModified)
.atZone(ZoneId.systemDefault()).toLocalDateTime();
if (lastUpdated == null || lastModifiedTime.isAfter(lastUpdated)) {
Expand All @@ -127,8 +133,11 @@ private void loadLatestDevModeViewsJsonIfNeeded() {
}

private void registerClientRoutes(LocalDateTime newLastUpdated) {
lastUpdated = newLastUpdated;
clientRouteRegistry.registerClientRoutes(deploymentConfiguration);
var hasClientRoutesRegistered = clientRouteRegistry
.registerClientRoutes(deploymentConfiguration);
if (hasClientRoutesRegistered) {
lastUpdated = newLastUpdated;
}
}

protected void collectServerViews(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public RouteUnifyingServiceInitListener(
public void serviceInit(ServiceInitEvent event) {
var deploymentConfiguration = event.getSource()
.getDeploymentConfiguration();
LOGGER.debug("deploymentConfiguration.isReactEnabled() = {}",
deploymentConfiguration.isReactEnabled());
if (deploymentConfiguration.isReactEnabled()) {
var routeUnifyingIndexHtmlRequestListener = new RouteUnifyingIndexHtmlRequestListener(
clientRouteRegistry, deploymentConfiguration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ClientRouteRegistryTest {
public void when_clearRoutes_isCalled_then_allRoutesAreCleared()
throws IOException {
mockDevelopmentMode();
createMockedDevModeViewsJson();
createMockedDevModeFileRouteJson();

clientRouteRegistry.registerClientRoutes(deploymentConfiguration);
Map<String, ClientViewConfig> allRoutes = clientRouteRegistry
Expand All @@ -43,7 +43,7 @@ public void when_clearRoutes_isCalled_then_allRoutesAreCleared()
}

@Test
public void when_developmentMode_and_noViewsJsonFile_then_noRoutesAreRegistered()
public void when_developmentMode_and_noFileRouteJsonFile_then_noRoutesAreRegistered()
throws IOException {

mockDevelopmentMode();
Expand All @@ -55,12 +55,12 @@ public void when_developmentMode_and_noViewsJsonFile_then_noRoutesAreRegistered(
}

@Test
public void when_developmentMode_and_emptyViewsJsonFile_then_noRoutesAreRegistered()
public void when_developmentMode_and_emptyFileRouteJsonFile_then_noRoutesAreRegistered()
throws IOException {

mockDevelopmentMode();

projectRoot.newFile("frontend/generated/views.json");
projectRoot.newFile("frontend/generated/file-routes.json");

clientRouteRegistry.registerClientRoutes(deploymentConfiguration);
Map<String, ClientViewConfig> allRoutes = clientRouteRegistry
Expand Down Expand Up @@ -106,7 +106,7 @@ public void when_developmentMode_then_loadClientViewsFromFrontendGenerated()
throws IOException {

mockDevelopmentMode();
createMockedDevModeViewsJson();
createMockedDevModeFileRouteJson();

clientRouteRegistry.registerClientRoutes(deploymentConfiguration);
Map<String, ClientViewConfig> allRoutes = clientRouteRegistry
Expand Down Expand Up @@ -145,17 +145,18 @@ private void mockDevelopmentMode() throws IOException {
.thenReturn(frontendGeneratedDir.getParentFile());
}

private void createMockedDevModeViewsJson() throws IOException {
var viewsJsonProdAsResource = getClass()
.getResource("/META-INF/VAADIN/views.json");
assert viewsJsonProdAsResource != null;
private void createMockedDevModeFileRouteJson() throws IOException {
var fileRoutesJsonProdAsResource = getClass()
.getResource(ClientRouteRegistry.FILE_ROUTES_JSON_PROD_PATH);
assert fileRoutesJsonProdAsResource != null;
String hierarchicalRoutesAsString = IOUtils.toString(
viewsJsonProdAsResource.openStream(), StandardCharsets.UTF_8);
fileRoutesJsonProdAsResource.openStream(),
StandardCharsets.UTF_8);
String addedDevToRootRoute = hierarchicalRoutesAsString
.replaceFirst("\"route\": \"\",", "\"route\": \"dev\",");
var viewsJsonFile = projectRoot
.newFile("frontend/generated/views.json");
try (PrintWriter writer = new PrintWriter(viewsJsonFile)) {
var fileRoutesJsonFile = projectRoot.newFile("frontend/generated/"
+ ClientRouteRegistry.FILE_ROUTES_JSON_NAME);
try (PrintWriter writer = new PrintWriter(fileRoutesJsonFile)) {
writer.println(addedDevToRootRoute);
}
}
Expand Down
11 changes: 5 additions & 6 deletions packages/ts/file-router/src/vite-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export default function vitePluginFileSystemRouter({
_logger.info(`The output directory: ${String(_outDir)}`);

runtimeUrls = {
json: new URL('views.json', isDevMode ? _generatedDir : _outDir),
code: new URL('views.ts', _generatedDir),
json: new URL('file-routes.json', isDevMode ? _generatedDir : _outDir),
code: new URL('file-routes.ts', _generatedDir),
};
},
async buildStart() {
Expand All @@ -89,10 +89,9 @@ export default function vitePluginFileSystemRouter({
return;
}

generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger).catch((e: unknown) =>
_logger.error(String(e)),
);
server.hot.send({ type: 'full-reload' });
generateRuntimeFiles(_viewsDir, runtimeUrls, extensions, _logger)
.then(() => server.hot.send({ type: 'full-reload' }))
.catch((e: unknown) => _logger.error(String(e)));
};

server.watcher.on('add', changeListener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ export default routes;
],
);

const file = createSourceFile(routeDeclaration, 'views.ts');

const file = createSourceFile(routeDeclaration, 'file-routes.ts');
// also keep the old file temporarily for compatibility purposes:
const tempFile = createSourceFile(routeDeclaration, 'views.ts');
printer.printFile(tempFile);
return printer.printFile(file);
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,17 @@ export async function generateRuntimeFiles(
logger.info('Collected file-based routes');
const runtimeRoutesCode = createRoutesFromMeta(routeMeta, urls);
const viewConfigJson = await createViewConfigJson(routeMeta);

const tempUrl = new URL('views.ts', urls.code.href);
await Promise.all([
generateRuntimeFile(urls.json, viewConfigJson).then(() =>
logger.info(`Frontend route list is generated: ${String(urls.json)}`),
),
generateRuntimeFile(urls.code, runtimeRoutesCode).then(() =>
logger.info(`Views module is generated: ${String(urls.code)}`),
logger.info(`File Route module is generated: ${String(urls.code)}`),
),
// also keep the old file temporarily for compatibility purposes:
generateRuntimeFile(tempUrl, runtimeRoutesCode).then(() =>
logger.info(`Views module is generated: ${String(tempUrl)}`),
),
]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ describe('@vaadin/hilla-file-router', () => {
dir = pathToFileURL(join(tmpdir(), 'file-router/'));
meta = createTestingRouteMeta(new URL('./views/', dir));
runtimeUrls = {
json: new URL('server/views.json', dir),
code: new URL('generated/views.ts', dir),
json: new URL('server/file-routes.json', dir),
code: new URL('generated/file-routes.ts', dir),
};
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ describe('@vaadin/hilla-file-router', () => {

viewsDir = new URL('views/', tmp);
runtimeUrls = {
json: new URL('server/views.json', tmp),
code: new URL('generated/views.ts', tmp),
json: new URL('server/file-routes.json', tmp),
code: new URL('generated/file-routes.ts', tmp),
};

await createTestingRouteFiles(viewsDir);
Expand Down
Loading