Skip to content

Commit 867825c

Browse files
caaladormshabarov
andauthored
Fix: update tsconfig and log message (#18987)
* Fix: update tsconfig and log message Instead of updating and throwing exception for 'tsconfig.json'. Generate .bak file, update file and log info if the file is updated. Fixes #18984 * unify backup file for ts config/definition both make a .bak file with a 'stamp'. Only log for tsDefinition instead of throwing. * Clean up commented code. Only log warning one time * Reset flag in tests * Minor cleanup * update message * Update flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskGenerateTsDefinitions.java --------- Co-authored-by: Mikhail Shabarov <61410877+mshabarov@users.noreply.github.com>
1 parent 7ebdf2e commit 867825c

File tree

4 files changed

+146
-51
lines changed

4 files changed

+146
-51
lines changed

flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskGenerateTsConfig.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
*/
4141
public class TaskGenerateTsConfig extends AbstractTaskClientGenerator {
4242

43+
/**
44+
* Keeps track of whether a warning update has already been logged. This is
45+
* used to avoid spamming the log with the same message.
46+
*/
47+
protected static boolean warningEmitted = false;
48+
4349
private static final String COMPILER_OPTIONS = "compilerOptions";
4450

4551
static final String TSCONFIG_JSON = "tsconfig.json";
@@ -52,15 +58,17 @@ public class TaskGenerateTsConfig extends AbstractTaskClientGenerator {
5258
"v23.3.0", "v23.2", "v23.1", "v22", "v14", "osgi", "v23.3.4",
5359
"v23.3.4-hilla" };
5460

55-
//@formatter:off
56-
static final String ERROR_MESSAGE =
57-
"%n%n**************************************************************************"
58-
+ "%n* TypeScript config file 'tsconfig.json' has been updated to the latest *"
59-
+ "%n* version by Vaadin. Please verify that the updated 'tsconfig.json' *"
60-
+ "%n* file contains configuration needed for your project (add missing part *"
61-
+ "%n* from the old file if necessary) and restart the application. *"
62-
+ "%n**************************************************************************%n%n";
63-
//@formatter:on
61+
static final String ERROR_MESSAGE = """
62+
63+
**************************************************************************
64+
* TypeScript config file 'tsconfig.json' has been updated to the latest *
65+
* version by Vaadin. Please verify that the updated 'tsconfig.json' *
66+
* file contains configuration needed for your project (add any missing *
67+
* parts from the old file if necessary) and restart the application. *
68+
* Old configuration is stored as a '.bak' file. *
69+
**************************************************************************
70+
71+
""";
6472

6573
private Options options;
6674

@@ -216,11 +224,18 @@ private void overrideIfObsolete() throws ExecutionFailedException {
216224
}
217225
}
218226

227+
File backupFile = File.createTempFile(
228+
projectTsConfigFile.getName() + ".", ".bak",
229+
projectTsConfigFile.getParentFile());
230+
FileIOUtils.writeIfChanged(backupFile, projectTsConfigAsString);
219231
// Project's TS config has a custom content -
220232
// rewrite and throw an exception with explanations
221233
FileIOUtils.writeIfChanged(projectTsConfigFile,
222234
latestTsConfigTemplate);
223-
throw new ExecutionFailedException(String.format(ERROR_MESSAGE));
235+
if (!warningEmitted) {
236+
log().warn(ERROR_MESSAGE);
237+
warningEmitted = true;
238+
}
224239
} catch (IOException e) {
225240
throw new UncheckedIOException(e);
226241
}

flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskGenerateTsDefinitions.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.IOException;
2020
import java.io.InputStream;
2121
import java.nio.file.Files;
22-
import java.nio.file.Path;
2322
import java.nio.file.StandardOpenOption;
2423
import java.util.regex.Pattern;
2524

@@ -43,21 +42,18 @@ public class TaskGenerateTsDefinitions extends AbstractTaskClientGenerator {
4342
private static final String DECLARE_CSS_MODULE = "declare module '*.css?inline' {";
4443
static final String UPDATE_MESSAGE = """
4544
46-
4745
***************************************************************************
4846
* The TypeScript type declaration file 'types.d.ts' has been updated *
4947
* to the latest version by Vaadin. Previous content has been backed up *
50-
* on 'types.d.ts.flowBackup' file. Please verify that the updated *
51-
* 'types.d.ts' file contains configuration needed for your project, and *
52-
* then delete the backup file. *
48+
* as a '.bak' file. Please verify that the updated 'types.d.ts' file *
49+
* contains configuration needed for your project, and then delete the *
50+
* backup file. *
5351
***************************************************************************
5452
55-
5653
""";
5754

5855
static final String CHECK_CONTENT_MESSAGE = """
5956
60-
6157
****************************************************************************
6258
* The TypeScript type declaration file 'types.d.ts' has been customized. *
6359
* Make sure the exact following configuration is present in that file: *
@@ -68,8 +64,12 @@ public class TaskGenerateTsDefinitions extends AbstractTaskClientGenerator {
6864
* and then update it with your custom contents. *
6965
****************************************************************************"
7066
71-
7267
""";
68+
/**
69+
* Keeps track of whether a warning update has already been logged. This is
70+
* used to avoid spamming the log with the same message.
71+
*/
72+
protected static boolean warningEmitted = false;
7373

7474
static final String TS_DEFINITIONS = "types.d.ts";
7575
static final Pattern COMMENT_LINE = Pattern.compile("(?m)^/[/*].*\\R");
@@ -148,11 +148,12 @@ private void updateIfContentMissing() throws ExecutionFailedException {
148148
"Updating custom {} to add '*.css?inline' module declaration",
149149
TS_DEFINITIONS);
150150
UpdateMode updateMode = computeUpdateMode(content);
151-
if (updateMode == UpdateMode.UPDATE_AND_THROW) {
151+
if (updateMode == UpdateMode.UPDATE_AND_BACKUP) {
152152
try {
153-
Path backupFile = tsDefinitions.toPath().getParent()
154-
.resolve(TS_DEFINITIONS + ".flowBackup");
155-
Files.copy(tsDefinitions.toPath(), backupFile);
153+
File backupFile = File.createTempFile(
154+
tsDefinitions.getName() + ".", ".bak",
155+
tsDefinitions.getParentFile());
156+
FileIOUtils.writeIfChanged(backupFile, content);
156157
log().debug("Created {} backup copy on {}",
157158
TS_DEFINITIONS, backupFile);
158159
} catch (IOException ex) {
@@ -171,8 +172,10 @@ private void updateIfContentMissing() throws ExecutionFailedException {
171172
Files.writeString(tsDefinitions.toPath(),
172173
uncommentedDefaultContent,
173174
StandardOpenOption.APPEND);
174-
if (updateMode == UpdateMode.UPDATE_AND_THROW) {
175-
throw new ExecutionFailedException(UPDATE_MESSAGE);
175+
if (updateMode == UpdateMode.UPDATE_AND_BACKUP
176+
&& !warningEmitted) {
177+
log().warn(UPDATE_MESSAGE);
178+
warningEmitted = true;
176179
}
177180
}
178181
} catch (IOException ex) {
@@ -185,7 +188,7 @@ private void updateIfContentMissing() throws ExecutionFailedException {
185188
}
186189

187190
private enum UpdateMode {
188-
REPLACE, UPDATE, UPDATE_AND_THROW
191+
REPLACE, UPDATE, UPDATE_AND_BACKUP
189192
}
190193

191194
private UpdateMode computeUpdateMode(String content)
@@ -220,7 +223,7 @@ private UpdateMode computeUpdateMode(String content)
220223
// types.d.ts has been customized, but does not seem to contain content
221224
// required by flow. Append what is needed and throw an exception so
222225
// that developer can check the changes are ok
223-
return UpdateMode.UPDATE_AND_THROW;
226+
return UpdateMode.UPDATE_AND_BACKUP;
224227
}
225228

226229
private static String removeComments(String content) {

flow-server/src/test/java/com/vaadin/flow/server/frontend/TaskGenerateTsConfigTest.java

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.vaadin.flow.server.frontend;
1717

18+
import static com.vaadin.flow.server.frontend.TaskGenerateTsConfig.ERROR_MESSAGE;
1819
import static java.nio.charset.StandardCharsets.UTF_8;
1920

2021
import java.io.File;
@@ -25,19 +26,22 @@
2526
import java.nio.file.Path;
2627
import java.util.Objects;
2728

29+
import net.jcip.annotations.NotThreadSafe;
2830
import org.apache.commons.io.FileUtils;
2931
import org.apache.commons.io.IOUtils;
3032
import org.junit.Assert;
3133
import org.junit.Before;
3234
import org.junit.Rule;
3335
import org.junit.Test;
3436
import org.junit.rules.TemporaryFolder;
37+
import org.mockito.MockedStatic;
3538
import org.mockito.Mockito;
3639

3740
import com.vaadin.experimental.FeatureFlags;
3841
import com.vaadin.flow.di.Lookup;
3942
import com.vaadin.flow.server.ExecutionFailedException;
4043

44+
@NotThreadSafe
4145
public class TaskGenerateTsConfigTest {
4246
static private String LATEST_VERSION = "9.1";
4347

@@ -57,6 +61,7 @@ public void setUp() throws IOException {
5761
.withFeatureFlags(featureFlags);
5862

5963
taskGenerateTsConfig = new TaskGenerateTsConfig(options);
64+
taskGenerateTsConfig.warningEmitted = false;
6065
}
6166

6267
@Test
@@ -204,20 +209,43 @@ public void tsConfigHasLatestVersion_noUpdates()
204209
}
205210

206211
@Test
207-
public void tsConfigHasCustomCodes_updatesAndThrows() throws IOException {
212+
public void tsConfigHasCustomCodes_updatesAndLogsWarning()
213+
throws IOException, ExecutionFailedException {
208214
File tsconfig = writeTestTsConfigContent(
209215
"tsconfig-custom-content.json");
210-
try {
216+
MockLogger logger = new MockLogger();
217+
try (MockedStatic<AbstractTaskClientGenerator> client = Mockito
218+
.mockStatic(AbstractTaskClientGenerator.class,
219+
Mockito.CALLS_REAL_METHODS)) {
220+
client.when(() -> AbstractTaskClientGenerator.log())
221+
.thenReturn(logger);
222+
taskGenerateTsConfig.execute();
223+
}
224+
String tsConfigString = FileUtils.readFileToString(tsconfig, UTF_8);
225+
Assert.assertTrue(tsConfigString.contains(
226+
"\"@vaadin/flow-frontend\": [\"generated/jar-resources\"],"));
227+
Assert.assertTrue(logger.getLogs().contains(ERROR_MESSAGE));
228+
}
229+
230+
@Test
231+
public void warningIsLoggedOnlyOncePerRun()
232+
throws IOException, ExecutionFailedException {
233+
File tsconfig = writeTestTsConfigContent(
234+
"tsconfig-custom-content.json");
235+
MockLogger logger = new MockLogger();
236+
try (MockedStatic<AbstractTaskClientGenerator> client = Mockito
237+
.mockStatic(AbstractTaskClientGenerator.class,
238+
Mockito.CALLS_REAL_METHODS)) {
239+
client.when(() -> AbstractTaskClientGenerator.log())
240+
.thenReturn(logger);
241+
taskGenerateTsConfig.execute();
242+
Assert.assertTrue(logger.getLogs().contains(ERROR_MESSAGE));
243+
logger.clearLogs();
244+
tsconfig.delete();
245+
writeTestTsConfigContent("tsconfig-custom-content.json");
211246
taskGenerateTsConfig.execute();
212-
} catch (Exception e) {
213-
Assert.assertTrue(e.getMessage().contains(
214-
"TypeScript config file 'tsconfig.json' has been updated to the latest"));
215-
String tsConfigString = FileUtils.readFileToString(tsconfig, UTF_8);
216-
Assert.assertTrue(tsConfigString.contains(
217-
"\"@vaadin/flow-frontend\": [\"generated/jar-resources\"],"));
218-
return;
247+
Assert.assertFalse(logger.getLogs().contains(ERROR_MESSAGE));
219248
}
220-
Assert.fail("Expected exception to be thrown");
221249
}
222250

223251
@Test

flow-server/src/test/java/com/vaadin/flow/server/frontend/TaskGenerateTsDefinitionsTest.java

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.nio.file.Path;
2323
import java.nio.file.attribute.FileTime;
2424

25+
import net.jcip.annotations.NotThreadSafe;
2526
import org.apache.commons.io.IOUtils;
2627
import org.hamcrest.CoreMatchers;
2728
import org.hamcrest.MatcherAssert;
@@ -30,6 +31,7 @@
3031
import org.junit.Rule;
3132
import org.junit.Test;
3233
import org.junit.rules.TemporaryFolder;
34+
import org.mockito.MockedStatic;
3335
import org.mockito.Mockito;
3436

3537
import com.vaadin.flow.di.Lookup;
@@ -38,6 +40,7 @@
3840
import static com.vaadin.flow.server.frontend.TaskGenerateTsDefinitions.TS_DEFINITIONS;
3941
import static java.nio.charset.StandardCharsets.UTF_8;
4042

43+
@NotThreadSafe
4144
public class TaskGenerateTsDefinitionsTest {
4245
@Rule
4346
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -50,6 +53,7 @@ public void setUp() throws IOException {
5053
Options options = new Options(Mockito.mock(Lookup.class), outputFolder);
5154

5255
taskGenerateTsDefinitions = new TaskGenerateTsDefinitions(options);
56+
taskGenerateTsDefinitions.warningEmitted = false;
5357
}
5458

5559
@Test
@@ -199,7 +203,7 @@ public void tsDefinition_oldHillaContents_ignoringSingleLineComments_tsDefinitio
199203
}
200204

201205
@Test
202-
public void customTsDefinition_missingFlowContents_tsDefinitionUpdatedAndExceptionThrown()
206+
public void customTsDefinition_missingFlowContents_tsDefinitionUpdatedAndWarningLogged()
203207
throws Exception {
204208
Path typesTSfile = new File(outputFolder, "types.d.ts").toPath();
205209
String originalContent = """
@@ -211,11 +215,17 @@ public void customTsDefinition_missingFlowContents_tsDefinitionUpdatedAndExcepti
211215
export type JTDForm = typeof jtdForms[number];
212216
""";
213217
Files.writeString(typesTSfile, originalContent);
214-
ExecutionFailedException exception = Assert.assertThrows(
215-
ExecutionFailedException.class,
216-
taskGenerateTsDefinitions::execute);
217-
MatcherAssert.assertThat(exception.getMessage(), CoreMatchers
218-
.containsString(TaskGenerateTsDefinitions.UPDATE_MESSAGE));
218+
219+
MockLogger logger = new MockLogger();
220+
try (MockedStatic<AbstractTaskClientGenerator> client = Mockito
221+
.mockStatic(AbstractTaskClientGenerator.class,
222+
Mockito.CALLS_REAL_METHODS)) {
223+
client.when(() -> AbstractTaskClientGenerator.log())
224+
.thenReturn(logger);
225+
taskGenerateTsDefinitions.execute();
226+
}
227+
Assert.assertTrue(logger.getLogs()
228+
.contains(TaskGenerateTsDefinitions.UPDATE_MESSAGE));
219229
Assert.assertFalse(
220230
"Should not generate types.d.ts when already existing",
221231
taskGenerateTsDefinitions.shouldGenerate());
@@ -227,17 +237,24 @@ public void customTsDefinition_missingFlowContents_tsDefinitionUpdatedAndExcepti
227237
}
228238

229239
@Test
230-
public void customTsDefinition_oldFlowContents_tsDefinitionUpdatedAndExceptionThrown()
240+
public void customTsDefinition_oldFlowContents_tsDefinitionUpdatedAndWarningLogged()
231241
throws Exception {
232242
Path typesTSfile = new File(outputFolder, "types.d.ts").toPath();
233243
String originalContent = "import type { SchemaObject } from \"../../types\";"
234244
+ System.lineSeparator() + readPreviousContent();
235245
Files.writeString(typesTSfile, originalContent);
236-
ExecutionFailedException exception = Assert.assertThrows(
237-
ExecutionFailedException.class,
238-
taskGenerateTsDefinitions::execute);
239-
MatcherAssert.assertThat(exception.getMessage(), CoreMatchers
240-
.containsString(TaskGenerateTsDefinitions.UPDATE_MESSAGE));
246+
247+
MockLogger logger = new MockLogger();
248+
try (MockedStatic<AbstractTaskClientGenerator> client = Mockito
249+
.mockStatic(AbstractTaskClientGenerator.class,
250+
Mockito.CALLS_REAL_METHODS)) {
251+
client.when(() -> AbstractTaskClientGenerator.log())
252+
.thenReturn(logger);
253+
taskGenerateTsDefinitions.execute();
254+
}
255+
Assert.assertTrue(logger.getLogs()
256+
.contains(TaskGenerateTsDefinitions.UPDATE_MESSAGE));
257+
241258
Assert.assertFalse(
242259
"Should not generate types.d.ts when already existing",
243260
taskGenerateTsDefinitions.shouldGenerate());
@@ -248,6 +265,35 @@ public void customTsDefinition_oldFlowContents_tsDefinitionUpdatedAndExceptionTh
248265
assertBackupFileCreated(originalContent);
249266
}
250267

268+
@Test
269+
public void contentUpdateForSecondTime_tsDefinitionUpdatedAndWarningLoggedOnce()
270+
throws Exception {
271+
Path typesTSfile = new File(outputFolder, "types.d.ts").toPath();
272+
String originalContent = "import type { SchemaObject } from \"../../types\";"
273+
+ System.lineSeparator() + readPreviousContent();
274+
Files.writeString(typesTSfile, originalContent);
275+
276+
MockLogger logger = new MockLogger();
277+
try (MockedStatic<AbstractTaskClientGenerator> client = Mockito
278+
.mockStatic(AbstractTaskClientGenerator.class,
279+
Mockito.CALLS_REAL_METHODS)) {
280+
client.when(() -> AbstractTaskClientGenerator.log())
281+
.thenReturn(logger);
282+
taskGenerateTsDefinitions.execute();
283+
284+
Assert.assertTrue(logger.getLogs()
285+
.contains(TaskGenerateTsDefinitions.UPDATE_MESSAGE));
286+
287+
logger.clearLogs();
288+
289+
Files.writeString(typesTSfile, originalContent);
290+
291+
taskGenerateTsDefinitions.execute();
292+
}
293+
Assert.assertFalse(logger.getLogs()
294+
.contains(TaskGenerateTsDefinitions.UPDATE_MESSAGE));
295+
}
296+
251297
@Test
252298
public void customTsDefinition_flowContents_tsDefinitionNotUpdated()
253299
throws Exception {
@@ -366,9 +412,12 @@ public void customTsDefinition_differentCSSModuleDefinition_tsDefinitionNotUpdat
366412

367413
private void assertBackupFileCreated(String originalContent)
368414
throws IOException {
369-
File backupFile = new File(
370-
taskGenerateTsDefinitions.getGeneratedFile().getParent(),
371-
TS_DEFINITIONS + ".flowBackup");
415+
File[] backups = taskGenerateTsDefinitions.getGeneratedFile()
416+
.getParentFile()
417+
.listFiles(file -> file.getName().endsWith(".bak"));
418+
Assert.assertEquals(1, backups.length);
419+
420+
File backupFile = backups[0];
372421
Assert.assertTrue("Original types.d.ts backup should exist",
373422
backupFile.exists());
374423
Assert.assertEquals(originalContent,

0 commit comments

Comments
 (0)