Skip to content

Commit

Permalink
fix: Split Vite configuration into a generated part and a customizabl…
Browse files Browse the repository at this point in the history
…e part (#12208)

Fixes #12115
  • Loading branch information
Artur- committed Nov 1, 2021
1 parent 64132a4 commit 4e28831
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 140 deletions.
Expand Up @@ -121,6 +121,11 @@ public class FrontendUtils {
*/
public static final String VITE_CONFIG = "vite.config.ts";

/**
* The name of the generated vite configuration file.
*/
public static final String VITE_GENERATED_CONFIG = "vite.generated.ts";

/**
* The name of the webpack generated configuration file.
*/
Expand Down
Expand Up @@ -22,18 +22,14 @@
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.vaadin.flow.server.frontend.TaskUpdateSettingsFile.DEV_SETTINGS_FILE;

/**
* Updates the Vite config file according with current project settings.
* Updates the Vite configuration files according with current project settings.
* <p>
* For internal use only. May be renamed or removed in a future release.
*/
Expand All @@ -42,9 +38,6 @@ public class TaskUpdateVite implements FallibleCommand, Serializable {
private File configFolder;
private String buildFolder;

Pattern frontendFilePattern = Pattern
.compile("import settings from '(.*)';");

TaskUpdateVite(File configFolder, String buildFolder) {
this.configFolder = configFolder;
this.buildFolder = buildFolder;
Expand All @@ -54,49 +47,40 @@ public class TaskUpdateVite implements FallibleCommand, Serializable {
public void execute() {
try {
createConfig();
createGeneratedConfig();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private void createConfig() throws IOException {
// Only create it if it does not exist
File configFile = new File(configFolder, FrontendUtils.VITE_CONFIG);
if (configFile.exists()) {
return;
}

if (!configFile.exists()) {
URL resource = this.getClass().getClassLoader()
.getResource(FrontendUtils.VITE_CONFIG);
String template = IOUtils.toString(resource,
StandardCharsets.UTF_8);

template = updateSettings(template);
FileUtils.write(configFile, template, StandardCharsets.UTF_8);
log().debug("Created vite configuration file: '{}'", configFile);
} else {
String template = IOUtils.toString(configFile.toURI(),
StandardCharsets.UTF_8);

String updatedTemplate = updateSettings(template);

if (!template.equals(updatedTemplate)) {
FileUtils.write(configFile, updatedTemplate,
StandardCharsets.UTF_8);
log().debug("Updated vite configuration settings path: '{}'",
configFile);
}
URL resource = this.getClass().getClassLoader()
.getResource(FrontendUtils.VITE_CONFIG);
String template = IOUtils.toString(resource, StandardCharsets.UTF_8);
FileUtils.write(configFile, template, StandardCharsets.UTF_8);
log().debug("Created vite configuration file: '{}'", configFile);

}
}

private String updateSettings(String template) {
final Matcher matcher = frontendFilePattern.matcher(template);
if (matcher.find() && !matcher.group(1)
.equals("./" + buildFolder + "/" + DEV_SETTINGS_FILE)) {
template = template.replace(
"import settings from '" + matcher.group(1) + "';",
"import settings from './" + buildFolder + "/"
+ DEV_SETTINGS_FILE + "';");
}
return template;
private void createGeneratedConfig() throws IOException {
// Always overwrite this
File generatedConfigFile = new File(configFolder,
FrontendUtils.VITE_GENERATED_CONFIG);
URL resource = this.getClass().getClassLoader()
.getResource(FrontendUtils.VITE_GENERATED_CONFIG);
String template = IOUtils.toString(resource, StandardCharsets.UTF_8);

template = template.replace("#settingsImport#", "./" + buildFolder + "/"
+ TaskUpdateSettingsFile.DEV_SETTINGS_FILE);
FileUtils.write(generatedConfigFile, template, StandardCharsets.UTF_8);
log().debug("Created vite generated configuration file: '{}'",
generatedConfigFile);
}

private Logger log() {
Expand Down
89 changes: 7 additions & 82 deletions flow-server/src/main/resources/vite.config.ts
@@ -1,84 +1,9 @@
import { defineConfig } from 'vite';
import path from 'path';
import { UserConfigFn } from 'vite';
import { overrideVaadinConfig } from './vite.generated';

import { processThemeResources } from '@vaadin/application-theme-plugin/theme-handle.js';
import settings from './target/vaadin-dev-server-settings.json';
const customConfig: UserConfigFn = (env) => ({
// Here you can add custom Vite parameters
// https://vitejs.dev/config/
});

const frontendFolder = path.resolve(__dirname, settings.frontendFolder);
const themeFolder = path.resolve(frontendFolder, settings.themeFolder);
const buildFolder = path.resolve(__dirname, settings.frontendBundleOutput);

const projectStaticAssetsFolders = [
path.resolve(__dirname, 'src', 'main', 'resources', 'META-INF', 'resources'),
path.resolve(__dirname, 'src', 'main', 'resources', 'static'),
frontendFolder
];

// Folders in the project which can contain application themes
const themeProjectFolders = projectStaticAssetsFolders.map((folder) =>
path.resolve(folder, settings.themeFolder)
);

const themeOptions = {
devMode: false,
// The following matches folder 'target/flow-frontend/themes/'
// (not 'frontend/themes') for theme in JAR that is copied there
themeResourceFolder: path.resolve(__dirname, settings.themeResourceFolder),
themeProjectFolders: themeProjectFolders,
projectStaticAssetsOutputFolder: path.resolve(__dirname, settings.staticOutput),
frontendGeneratedFolder: path.resolve(frontendFolder, settings.generatedFolder)
};

// Block debug and trace logs.
console.trace = () => {};
console.debug =() => {};

function updateTheme(contextPath: string) {
const themePath = path.resolve(themeFolder);
if(contextPath.startsWith(themePath)) {
const changed = path.relative(themePath, contextPath);

console.debug("Theme file changed", changed);

if(changed.startsWith(settings.themeName)) {
processThemeResources(themeOptions, console);
}
}
}

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => ({
root: 'frontend',
base: mode === 'production' ? '' : '/VAADIN/',
resolve: {
alias: {
themes: themeFolder,
Frontend: frontendFolder
},
},
build: {
outDir: buildFolder,
assetsDir: 'VAADIN/build',
rollupOptions: {
input: {
main: path.resolve(frontendFolder, 'index.html'),
generated: path.resolve(frontendFolder, 'generated/vaadin.ts')
},
output: {
// Produce only one chunk that gets imported into index.html
manualChunks: () => 'everything.js'
},
},
},
plugins: [
{
name: 'custom-theme',
config() {
processThemeResources(themeOptions, console);
},
handleHotUpdate(context) {
updateTheme(path.resolve(context.file));
}
}
]
}));
export default overrideVaadinConfig(customConfig);
99 changes: 99 additions & 0 deletions flow-server/src/main/resources/vite.generated.ts
@@ -0,0 +1,99 @@
/**
* NOTICE: this is an auto-generated file
*
* This file has been generated by the `flow:prepare-frontend` maven goal.
* This file will be overwritten on every run. Any custom changes should be made to vite.config.ts
*/
import path from 'path';

import { processThemeResources } from '@vaadin/application-theme-plugin/theme-handle.js';
import settings from '#settingsImport#';
import { UserConfigFn } from 'vite';
import { defineConfig } from 'vite';

const frontendFolder = path.resolve(__dirname, settings.frontendFolder);
const themeFolder = path.resolve(frontendFolder, settings.themeFolder);
const buildFolder = path.resolve(__dirname, settings.frontendBundleOutput);

const projectStaticAssetsFolders = [
path.resolve(__dirname, 'src', 'main', 'resources', 'META-INF', 'resources'),
path.resolve(__dirname, 'src', 'main', 'resources', 'static'),
frontendFolder
];

// Folders in the project which can contain application themes
const themeProjectFolders = projectStaticAssetsFolders.map((folder) =>
path.resolve(folder, settings.themeFolder)
);

const themeOptions = {
devMode: false,
// The following matches folder 'target/flow-frontend/themes/'
// (not 'frontend/themes') for theme in JAR that is copied there
themeResourceFolder: path.resolve(__dirname, settings.themeResourceFolder),
themeProjectFolders: themeProjectFolders,
projectStaticAssetsOutputFolder: path.resolve(__dirname, settings.staticOutput),
frontendGeneratedFolder: path.resolve(frontendFolder, settings.generatedFolder)
};

// Block debug and trace logs.
console.trace = () => {};
console.debug =() => {};

function updateTheme(contextPath: string) {
const themePath = path.resolve(themeFolder);
if(contextPath.startsWith(themePath)) {
const changed = path.relative(themePath, contextPath);

console.debug("Theme file changed", changed);

if(changed.startsWith(settings.themeName)) {
processThemeResources(themeOptions, console);
}
}
}

export const vaadinConfig:UserConfigFn = (env) => ({
root: 'frontend',
base: env.mode === 'production' ? '' : '/VAADIN/',
resolve: {
alias: {
themes: themeFolder,
Frontend: frontendFolder
},
},
build: {
outDir: buildFolder,
assetsDir: 'VAADIN/build',
rollupOptions: {
input: {
main: path.resolve(frontendFolder, 'index.html'),
generated: path.resolve(frontendFolder, 'generated/vaadin.ts')
},
output: {
// Produce only one chunk that gets imported into index.html
manualChunks: () => 'everything.js'
},
},
},
plugins: [
{
name: 'custom-theme',
config() {
processThemeResources(themeOptions, console);
},
handleHotUpdate(context) {
updateTheme(path.resolve(context.file));
}
}
]
});


export const overrideVaadinConfig = (customConfig:UserConfigFn) => {
return defineConfig((env) => ({
...vaadinConfig(env),
...customConfig(env)
}));

}
Expand Up @@ -32,7 +32,7 @@ public void generatedTemplate_correctSettingsPath() throws IOException {
task.execute();

File configFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_CONFIG);
FrontendUtils.VITE_GENERATED_CONFIG);

String template = IOUtils.toString(configFile.toURI(),
StandardCharsets.UTF_8);
Expand All @@ -42,12 +42,10 @@ public void generatedTemplate_correctSettingsPath() throws IOException {
}

@Test
public void templateExists_correctSettingsPath_fileNotWritten()
throws IOException {
public void configFileExists_fileNotOverwritten() throws IOException {
File configFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_CONFIG);
final String importString = "import settings from './build/"
+ DEV_SETTINGS_FILE + "';";
final String importString = "Hello Fake configuration";
FileUtils.write(configFile, importString, StandardCharsets.UTF_8);

new TaskUpdateVite(temporaryFolder.getRoot(), "build").execute();
Expand All @@ -60,22 +58,21 @@ public void templateExists_correctSettingsPath_fileNotWritten()
}

@Test
public void templateExists_faultySettingsPath_onlyPathUpdated()
public void generatedConfigFileExists_alwaysOverwritten()
throws IOException {
File configFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_CONFIG);
final String importString = "import settings from './target/"
+ DEV_SETTINGS_FILE + "';";
FileUtils.write(configFile, importString, StandardCharsets.UTF_8);
File generatedConfigFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_GENERATED_CONFIG);
final String importString = "Hello Fake generated configuration";
FileUtils.write(generatedConfigFile, importString,
StandardCharsets.UTF_8);

new TaskUpdateVite(temporaryFolder.getRoot(), "build").execute();

String template = IOUtils.toString(configFile.toURI(),
String template = IOUtils.toString(generatedConfigFile.toURI(),
StandardCharsets.UTF_8);

Assert.assertEquals("Settings file content was added.",
"import settings from './build/" + DEV_SETTINGS_FILE + "';",
template);
Assert.assertNotEquals("Generated file should have been overwritten",
importString, template);
}

@Test
Expand All @@ -85,10 +82,10 @@ public void usedSettings_matchThoseCreatedToSettingsFile()
"build");
task.execute();

File configFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_CONFIG);
File generatedConfigFile = new File(temporaryFolder.getRoot(),
FrontendUtils.VITE_GENERATED_CONFIG);

String template = IOUtils.toString(configFile.toURI(),
String template = IOUtils.toString(generatedConfigFile.toURI(),
StandardCharsets.UTF_8);
NodeTasks.Builder builder = new NodeTasks.Builder(
Mockito.mock(Lookup.class), temporaryFolder.getRoot(),
Expand Down

0 comments on commit 4e28831

Please sign in to comment.