Skip to content

Commit

Permalink
Merge pull request #10914 from murdos/react-depends-on-typescript
Browse files Browse the repository at this point in the history
feat: react module now depends on typescript module
  • Loading branch information
pascalgrimaud authored Sep 20, 2024
2 parents 973342e + c3bcc11 commit 7d9a0ba
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
import static tech.jhipster.lite.module.domain.JHipsterModule.*;
import static tech.jhipster.lite.module.domain.npm.JHLiteNpmVersionSource.COMMON;
import static tech.jhipster.lite.module.domain.npm.JHLiteNpmVersionSource.REACT;
import static tech.jhipster.lite.module.domain.packagejson.NodeModuleFormat.MODULE;

import java.util.function.Consumer;
import tech.jhipster.lite.module.domain.Indentation;
import tech.jhipster.lite.module.domain.JHipsterModule;
import tech.jhipster.lite.module.domain.file.JHipsterDestination;
import tech.jhipster.lite.module.domain.file.JHipsterSource;
import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
import tech.jhipster.lite.module.domain.replacement.MandatoryReplacer;

public class ReactCoreModulesFactory {

Expand All @@ -27,56 +29,38 @@ public class ReactCoreModulesFactory {
private static final JHipsterDestination PRIMARY_APP_DESTINATION = APP_DESTINATION.append(PRIMARY_APP);

private static final String TEST_PRIMARY = "src/test/webapp/unit/common/primary/app";
private static final String DEFAULT_TSCONFIG_PATH = "\"@/*\": [\"src/main/webapp/app/*\"]";

public JHipsterModule buildModule(JHipsterModuleProperties properties) {
//@formatter:off
return moduleBuilder(properties)
.preCommitActions(stagedFilesFilter("{src/**/,}*.{ts,tsx}"), preCommitCommands("eslint --fix", "prettier --write"))
.packageJson()
.type(MODULE)
.removeDevDependency(packageName("@tsconfig/recommended"))
.addDevDependency(packageName("@testing-library/dom"), REACT)
.addDevDependency(packageName("@testing-library/react"), REACT)
.addDevDependency(packageName("@types/node"), COMMON)
.addDevDependency(packageName("@types/react"), REACT)
.addDevDependency(packageName("@types/react-dom"), REACT)
.addDevDependency(packageName("@tsconfig/vite-react"), REACT)
.addDevDependency(packageName("@typescript-eslint/eslint-plugin"), COMMON)
.addDevDependency(packageName("@vitejs/plugin-react"), REACT)
.addDevDependency(packageName("@vitest/coverage-istanbul"), COMMON)
.addDevDependency(packageName("typescript-eslint"), COMMON)
.addDevDependency(packageName("globals"), COMMON)
.addDevDependency(packageName("eslint"), COMMON)
.addDevDependency(packageName("eslint-plugin-react"), REACT)
.addDevDependency(packageName("jsdom"), COMMON)
.addDevDependency(packageName("typescript"), COMMON)
.addDevDependency(packageName("ts-node"), REACT)
.addDevDependency(packageName("vite"), COMMON)
.addDevDependency(packageName("vite-tsconfig-paths"), COMMON)
.addDevDependency(packageName("vitest"), COMMON)
.addDevDependency(packageName("vitest-sonar-reporter"), COMMON)
.addDependency(packageName("react"), REACT)
.addDependency(packageName("react-dom"), REACT)
.addDevDependency(packageName("npm-run-all2"), COMMON)
.addScript(scriptKey("dev"), scriptCommand("npm-run-all dev:*"))
.addScript(scriptKey("dev:vite"), scriptCommand("vite"))
.addScript(scriptKey("build"), scriptCommand("npm-run-all build:*"))
.addScript(scriptKey("build:tsc"), scriptCommand("tsc"))
.addScript(scriptKey("build:vite"), scriptCommand("vite build --emptyOutDir"))
.addScript(scriptKey("preview"), scriptCommand("vite preview"))
.addScript(scriptKey("start"), scriptCommand("vite"))
.addScript(scriptKey("lint"), scriptCommand("eslint ."))
.addScript(scriptKey("watch"), scriptCommand("npm-run-all --parallel watch:*"))
.addScript(scriptKey("watch:tsc"), scriptCommand("tsc --noEmit --watch"))
.addScript(scriptKey("test"), scriptCommand("npm run watch:test"))
.addScript(scriptKey("test:coverage"), scriptCommand("vitest run --coverage"))
.addScript(scriptKey("watch:test"), scriptCommand("vitest --"))
.and()
.files()
.batch(SOURCE, to("."))
.addFile("tsconfig.json")
.addTemplate("vite.config.ts")
.addTemplate("vitest.config.ts")
.addTemplate("eslint.config.js")
.and()
.batch(APP_SOURCE, APP_DESTINATION)
.addTemplate("index.css")
Expand All @@ -92,7 +76,93 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) {
.addFile("ReactLogo.png")
.and()
.and()
.apply(patchEslintConfig(properties))
.apply(patchTsConfig(properties))
.apply(patchVitestConfig(properties))
.build();
//@formatter:on
}

private Consumer<JHipsterModuleBuilder> patchEslintConfig(JHipsterModuleProperties properties) {
String reactConfig =
"""
\t\tfiles: ['src/main/webapp/**/*.{ts,tsx}', 'src/main/webapp/**/*.spec.ts'],
\t\textends: [...typescript.configs.recommendedTypeChecked, react],
\t\tsettings: {
\t\t\treact: {
\t\t\t\tversion: 'detect',
\t\t\t},
\t\t},\
""".replace("\t", properties.indentation().spaces());
//@formatter:off
return moduleBuilder -> moduleBuilder
.mandatoryReplacements()
.in(path("eslint.config.js"))
.add(lineAfterRegex("from 'typescript-eslint'"), "import react from 'eslint-plugin-react/configs/recommended.js';")
.add(regex("\\s+\\.\\.\\.typescript\\.configs\\.recommended.*"), "")
.add(regex("[ \\t]+files: \\['src/\\*/webapp/\\*\\*\\/\\*\\.ts'],"), reactConfig)
.add(
lineAfterRegex("globals: \\{ \\.\\.\\.globals\\.browser },"),
"""
\t\t\tparserOptions: {
\t\t\t\tproject: ['./tsconfig.json'],
\t\t\t},\
""".replace("\t", properties.indentation().spaces())
)
.add(
regex("[ \\t]+quotes: \\['error', 'single', \\{ avoidEscape: true }],"),
"""
\t\t\t'react/react-in-jsx-scope': 'off',
\t\t\t'@typescript-eslint/no-misused-promises': [
\t\t\t\t'error',
\t\t\t\t{
\t\t\t\t\tchecksVoidReturn: false,
\t\t\t\t}
\t\t\t],\
""".replace("\t", properties.indentation().spaces())
)
.and()
.and();
//@formatter:on
}

private Consumer<JHipsterModuleBuilder> patchTsConfig(JHipsterModuleProperties properties) {
String pathsReplacement =
DEFAULT_TSCONFIG_PATH + "," + LINE_BREAK + properties.indentation().times(3) + "\"@assets/*\": [\"src/main/webapp/assets/*\"]";
//@formatter:off
return moduleBuilder -> moduleBuilder
.mandatoryReplacements()
.in(path("tsconfig.json"))
.add(text("@tsconfig/recommended/tsconfig.json"), "@tsconfig/vite-react/tsconfig.json")
.add(tsConfigCompilerOption("composite", false, properties.indentation()))
.add(tsConfigCompilerOption("forceConsistentCasingInFileNames", true, properties.indentation()))
.add(tsConfigCompilerOption("allowSyntheticDefaultImports", true, properties.indentation()))
.add(text(DEFAULT_TSCONFIG_PATH), pathsReplacement)
.and()
.and();
//@formatter:on
}

private static MandatoryReplacer tsConfigCompilerOption(String optionName, boolean optionValue, Indentation indentation) {
String compilerOption = indentation.times(2) + "\"%s\": %s,".formatted(optionName, optionValue);
return new MandatoryReplacer(lineAfterRegex("\"compilerOptions\":"), compilerOption);
}

private Consumer<JHipsterModuleBuilder> patchVitestConfig(JHipsterModuleProperties properties) {
//@formatter:off
return moduleBuilder -> moduleBuilder
.mandatoryReplacements()
.in(path("vitest.config.ts"))
.add(lineAfterRegex("from 'vitest/config';"), "import react from '@vitejs/plugin-react';")
.add(text( "plugins: ["), "plugins: [react(), ")
.add(text("environment: 'node',"), "environment: 'jsdom',")
.add(vitestCoverageExclusion(properties,"src/main/webapp/app/index.tsx"))
.and();
//@formatter:on
}

private static MandatoryReplacer vitestCoverageExclusion(JHipsterModuleProperties properties, String filePattern) {
Indentation indentation = properties.indentation();
return new MandatoryReplacer(lineAfterRegex("configDefaults.coverage.exclude"), indentation.times(4) + "'" + filePattern + "',");
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package tech.jhipster.lite.generator.client.react.core.infrastructure.primary;

import static tech.jhipster.lite.generator.slug.domain.JHLiteFeatureSlug.*;
import static tech.jhipster.lite.generator.slug.domain.JHLiteFeatureSlug.CLIENT_CORE;
import static tech.jhipster.lite.generator.slug.domain.JHLiteModuleSlug.*;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tech.jhipster.lite.generator.client.react.core.application.ReactCoreApplicationService;
import tech.jhipster.lite.module.domain.resource.JHipsterModuleOrganization;
import tech.jhipster.lite.module.domain.resource.JHipsterModulePropertiesDefinition;
import tech.jhipster.lite.module.domain.resource.JHipsterModuleResource;
import tech.jhipster.lite.module.domain.resource.*;

@Configuration
class ReactCoreModulesConfiguration {
Expand All @@ -21,7 +19,7 @@ JHipsterModuleResource reactCoreModule(ReactCoreApplicationService react) {
.slug(REACT_CORE)
.propertiesDefinition(properties())
.apiDoc("Frontend - React", "Add React+Vite with minimal CSS")
.organization(JHipsterModuleOrganization.builder().feature(CLIENT_CORE).addDependency(INIT).addDependency(PRETTIER).build())
.organization(JHipsterModuleOrganization.builder().feature(CLIENT_CORE).addDependency(TYPESCRIPT).addDependency(PRETTIER).build())
.tags("client", REACT)
.factory(react::buildModule);
}
Expand Down
1 change: 1 addition & 0 deletions src/test/features/client/react-i18n.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Feature: React i18n
Scenario: Should apply react i18n module to react
When I apply modules to default project
| init |
| typescript |
| react-core |
| react-i18next |
Then I should have files in "src/main/webapp/app"
Expand Down
1 change: 1 addition & 0 deletions src/test/features/client/react-jwt.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Feature: React JWT
Scenario: Should apply react jwt module to react
When I apply modules to default project
| init |
| typescript |
| react-core |
| react-jwt |
Then I should have files in "src/main/webapp/app/common/services"
Expand Down
1 change: 1 addition & 0 deletions src/test/features/client/react.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Feature: React modules
When I apply modules to default project
| init |
| prettier |
| typescript |
| react-core |
Then I should have files in "src/main/webapp/app/common/primary/app"
| App.tsx |
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,29 @@ void shouldBuildModuleWithStyle() {
JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest()).projectBaseName("jhipster").build()
);

assertThatModuleWithFiles(module, packageJsonFile(), lintStagedConfigFile())
assertThatModuleWithFiles(module, packageJsonFile(), lintStagedConfigFile(), eslintConfigFile(), tsConfigFile(), vitestConfigFile())
.hasFile("package.json")
.containing("\"type\": \"module\"")
.notContaining(nodeDependency("@tsconfig/recommended"))
.containing(nodeDependency("@testing-library/dom"))
.containing(nodeDependency("@testing-library/react"))
.containing(nodeDependency("@types/node"))
.containing(nodeDependency("@types/react"))
.containing(nodeDependency("@types/react-dom"))
.containing(nodeDependency("@tsconfig/vite-react"))
.containing(nodeDependency("@typescript-eslint/eslint-plugin"))
.containing(nodeDependency("@vitejs/plugin-react"))
.containing(nodeDependency("@vitest/coverage-istanbul"))
.containing(nodeDependency("typescript-eslint"))
.containing(nodeDependency("globals"))
.containing(nodeDependency("eslint"))
.containing(nodeDependency("eslint-plugin-react"))
.containing(nodeDependency("jsdom"))
.containing(nodeDependency("typescript"))
.containing(nodeDependency("ts-node"))
.containing(nodeDependency("vite"))
.containing(nodeDependency("vite-tsconfig-paths"))
.containing(nodeDependency("vitest"))
.containing(nodeDependency("vitest-sonar-reporter"))
.containing(nodeDependency("react"))
.containing(nodeDependency("npm-run-all2"))
.containing(nodeDependency("react-dom"))
.containing(nodeScript("dev", "npm-run-all dev:*"))
.containing(nodeScript("dev:vite", "vite"))
.containing(nodeScript("build", "npm-run-all build:*"))
.containing(nodeScript("build:tsc", "tsc"))
.containing(nodeScript("build:vite", "vite build --emptyOutDir"))
.containing(nodeScript("watch", "npm-run-all --parallel watch:*"))
.containing(nodeScript("watch:tsc", "tsc --noEmit --watch"))
.containing(nodeScript("preview", "vite preview"))
.containing(nodeScript("start", "vite"))
.containing(nodeScript("lint", "eslint ."))
.containing(nodeScript("test", "npm run watch:test"))
.containing(nodeScript("test:coverage", "vitest run --coverage"))
.containing(nodeScript("watch:test", "vitest --"))
.and()
.hasFile(".lintstagedrc.cjs")
.containing(
Expand All @@ -69,7 +53,16 @@ void shouldBuildModuleWithStyle() {
"""
)
.and()
.hasFiles("tsconfig.json", "vite.config.ts", "vitest.config.ts", "eslint.config.js")
.hasFile("eslint.config.js")
.matchingSavedSnapshot()
.and()
.hasFile("tsconfig.json")
.matchingSavedSnapshot()
.and()
.hasFile("vitest.config.ts")
.matchingSavedSnapshot()
.and()
.hasFiles("vite.config.ts")
.hasFiles("src/main/webapp/index.html")
.hasPrefixedFiles("src/main/webapp/app", "index.css", "index.tsx", "vite-env.d.ts")
.hasFiles("src/test/webapp/unit/common/primary/app/App.spec.tsx")
Expand All @@ -86,7 +79,7 @@ void shouldViteConfigBeUpdatedWhenServerPortPropertyNotDefault() {
JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest()).projectBaseName("jhipster").put("serverPort", 8081).build()
);

assertThatModuleWithFiles(module, packageJsonFile())
assertThatModuleWithFiles(module, packageJsonFile(), eslintConfigFile(), tsConfigFile(), vitestConfigFile())
.hasFile("vite.config.ts")
.containing("localhost:8081")
.notContaining("localhost:8080");
Expand All @@ -98,7 +91,9 @@ void shouldViteConfigBeDefaultWhenServerPortPropertyMissing() {
JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest()).projectBaseName("jhipster").build()
);

assertThatModuleWithFiles(module, packageJsonFile()).hasFile("vite.config.ts").containing("localhost:8080");
assertThatModuleWithFiles(module, packageJsonFile(), eslintConfigFile(), tsConfigFile(), vitestConfigFile())
.hasFile("vite.config.ts")
.containing("localhost:8080");
}

private String nodeScript(String key, String value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import js from '@eslint/js';
import globals from 'globals';
import typescript from 'typescript-eslint';
import eslint from '@eslint/js';
import react from 'eslint-plugin-react/configs/recommended.js';

export default typescript.config(
Expand All @@ -12,9 +12,9 @@ export default typescript.config(
},
},
{
ignores: ['{{projectBuildDirectory}}/'],
ignores: ['target/'],
},
eslint.configs.recommended,
js.configs.recommended,
{
files: ['src/main/webapp/**/*.{ts,tsx}', 'src/main/webapp/**/*.spec.ts'],
extends: [...typescript.configs.recommendedTypeChecked, react],
Expand All @@ -24,9 +24,7 @@ export default typescript.config(
},
},
languageOptions: {
globals: {
...globals.browser,
},
globals: { ...globals.browser },
parserOptions: {
project: ['./tsconfig.json'],
},
Expand All @@ -36,7 +34,7 @@ export default typescript.config(
'@typescript-eslint/no-misused-promises': [
'error',
{
'checksVoidReturn': false
checksVoidReturn: false,
}
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"composite": false,
"baseUrl": ".",
"types": ["vitest/globals"],
"baseUrl": ".",
"paths": {
"@/*": ["src/main/webapp/app/*"],
"@assets/*": ["src/main/webapp/assets/*"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default defineConfig({
test: {
reporters: ['verbose', 'vitest-sonar-reporter'],
outputFile: {
'vitest-sonar-reporter': '{{projectBuildDirectory}}/test-results/TESTS-results-sonar.xml',
'vitest-sonar-reporter': 'target/test-results/TESTS-results-sonar.xml',
},
globals: true,
logHeapUsage: true,
Expand All @@ -34,7 +34,7 @@ export default defineConfig({
'src/main/webapp/app/index.tsx',
],
provider: 'istanbul',
reportsDirectory: '{{projectBuildDirectory}}/test-results/',
reportsDirectory: 'target/test-results/',
reporter: ['html', 'json-summary', 'text', 'text-summary', 'lcov', 'clover'],
watermarks: {
statements: [100, 100],
Expand Down
1 change: 1 addition & 0 deletions tests-ci/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ elif [[ $application == 'reactapp' ]]; then
frontend_server_plugin
applyModules \
"prettier" \
"typescript" \
"react-core" \
"cypress-component-tests"

Expand Down

0 comments on commit 7d9a0ba

Please sign in to comment.