aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-04-09 00:53:53 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-04-09 00:53:53 +0200
commitc3fcc1ae3d2f680a973e66c138d9be7ae22eee26 (patch)
tree6a30b2478a30747cd7a2522acdff941394dea4e7
parentbuild: always prefer slf4j instead of log4j 1.x (diff)
downloadrefinery-c3fcc1ae3d2f680a973e66c138d9be7ae22eee26.tar.gz
refinery-c3fcc1ae3d2f680a973e66c138d9be7ae22eee26.tar.zst
refinery-c3fcc1ae3d2f680a973e66c138d9be7ae22eee26.zip
build: refactor frontend build
* Always write ESLint output to a file in addition to the console to make the lintFrontend task cacheable in Gradle (according to the output file). * Make sure frontend task inputs are declared properly for caching. * Make sure Typescript type checking is incremental. * Do not use @tsconfig, because both Vite and SonarScanner have problems with extending tsconfig files from Yarn PnP modules.
-rw-r--r--subprojects/frontend/.eslintrc.cjs3
-rw-r--r--subprojects/frontend/build.gradle.kts75
-rw-r--r--subprojects/frontend/config/eslintReport.cjs52
-rw-r--r--subprojects/frontend/package.json4
-rw-r--r--subprojects/frontend/tsconfig.base.json24
-rw-r--r--subprojects/frontend/tsconfig.json1
-rw-r--r--subprojects/frontend/tsconfig.node.json1
-rw-r--r--subprojects/frontend/tsconfig.shared.json2
-rw-r--r--yarn.lock8
9 files changed, 119 insertions, 51 deletions
diff --git a/subprojects/frontend/.eslintrc.cjs b/subprojects/frontend/.eslintrc.cjs
index 8a7b474a..dc03d721 100644
--- a/subprojects/frontend/.eslintrc.cjs
+++ b/subprojects/frontend/.eslintrc.cjs
@@ -90,6 +90,7 @@ module.exports = {
90 files: [ 90 files: [
91 '.eslintrc.cjs', 91 '.eslintrc.cjs',
92 'config/*.ts', 92 'config/*.ts',
93 'config/*.cjs',
93 'prettier.config.cjs', 94 'prettier.config.cjs',
94 'vite.config.ts', 95 'vite.config.ts',
95 ], 96 ],
@@ -103,6 +104,8 @@ module.exports = {
103 'error', 104 'error',
104 { devDependencies: true }, 105 { devDependencies: true },
105 ], 106 ],
107 // Allow writing to the console in ad-hoc scripts.
108 'no-console': 'off',
106 // Access to the environment in configuration files. 109 // Access to the environment in configuration files.
107 'no-process-env': 'off', 110 'no-process-env': 'off',
108 }, 111 },
diff --git a/subprojects/frontend/build.gradle.kts b/subprojects/frontend/build.gradle.kts
index 4a51c74e..8d2a8631 100644
--- a/subprojects/frontend/build.gradle.kts
+++ b/subprojects/frontend/build.gradle.kts
@@ -18,15 +18,45 @@ val productionAssets: Configuration by configurations.creating {
18 isCanBeResolved = false 18 isCanBeResolved = false
19} 19}
20 20
21val sourcesWithoutTypeGen = fileTree("src") { 21val sourcesWithoutTypes = fileTree("src") {
22 exclude("**/*.typegen.ts") 22 exclude("**/*.typegen.ts")
23} 23}
24 24
25val sourcesWithTypes = fileTree("src") + fileTree("types")
26
27val buildScripts = fileTree("config") + files(
28 ".eslintrc.cjs",
29 "prettier.config.cjs",
30 "vite.config.ts",
31)
32
33val installationState = files(
34 rootProject.file("yarn.lock"),
35 rootProject.file("package.json"),
36 "package.json",
37)
38
39val sharedConfigFiles = installationState + files(
40 "tsconfig.json",
41 "tsconfig.base.json",
42 "tsconfig.node.json",
43 "tsconfig.shared.json",
44)
45
46val assembleConfigFiles = sharedConfigFiles + file("vite.config.ts") + fileTree("config") {
47 include("**/*.ts")
48}
49
50val assembleSources = sourcesWithTypes + fileTree("public") + file("index.html")
51
52val assembleFiles = assembleSources + assembleConfigFiles
53
54val lintingFiles = sourcesWithTypes + buildScripts + sharedConfigFiles
55
25val generateXStateTypes by tasks.registering(RunYarn::class) { 56val generateXStateTypes by tasks.registering(RunYarn::class) {
26 dependsOn(tasks.installFrontend) 57 dependsOn(tasks.installFrontend)
27 inputs.files(sourcesWithoutTypeGen) 58 inputs.files(sourcesWithoutTypes)
28 inputs.file("package.json") 59 inputs.files(installationState)
29 inputs.file(rootProject.file("yarn.lock"))
30 outputs.dir("src") 60 outputs.dir("src")
31 script.set("run typegen") 61 script.set("run typegen")
32 description = "Generate TypeScript typings for XState state machines." 62 description = "Generate TypeScript typings for XState state machines."
@@ -34,11 +64,7 @@ val generateXStateTypes by tasks.registering(RunYarn::class) {
34 64
35tasks.assembleFrontend { 65tasks.assembleFrontend {
36 dependsOn(generateXStateTypes) 66 dependsOn(generateXStateTypes)
37 inputs.dir("public") 67 inputs.files(assembleFiles)
38 inputs.files(sourcesWithoutTypeGen)
39 inputs.file("index.html")
40 inputs.files("package.json", "tsconfig.json", "tsconfig.base.json", "vite.config.ts")
41 inputs.file(rootProject.file("yarn.lock"))
42 outputs.dir(productionResources) 68 outputs.dir(productionResources)
43} 69}
44 70
@@ -51,10 +77,7 @@ artifacts {
51val typeCheckFrontend by tasks.registering(RunYarn::class) { 77val typeCheckFrontend by tasks.registering(RunYarn::class) {
52 dependsOn(tasks.installFrontend) 78 dependsOn(tasks.installFrontend)
53 dependsOn(generateXStateTypes) 79 dependsOn(generateXStateTypes)
54 inputs.dir("src") 80 inputs.files(lintingFiles)
55 inputs.dir("types")
56 inputs.files("package.json", "tsconfig.json", "tsconfig.base.json", "tsconfig.node.json")
57 inputs.file(rootProject.file("yarn.lock"))
58 outputs.dir("$buildDir/typescript") 81 outputs.dir("$buildDir/typescript")
59 script.set("run typecheck") 82 script.set("run typecheck")
60 group = "verification" 83 group = "verification"
@@ -65,17 +88,9 @@ val lintFrontend by tasks.registering(RunYarn::class) {
65 dependsOn(tasks.installFrontend) 88 dependsOn(tasks.installFrontend)
66 dependsOn(generateXStateTypes) 89 dependsOn(generateXStateTypes)
67 dependsOn(typeCheckFrontend) 90 dependsOn(typeCheckFrontend)
68 inputs.dir("src") 91 inputs.files(lintingFiles)
69 inputs.dir("types") 92 outputs.file("$buildDir/eslint.json")
70 inputs.files(".eslintrc.cjs", "prettier.config.cjs") 93 script.set("run lint")
71 inputs.files("package.json", "tsconfig.json", "tsconfig.base.json", "tsconfig.node.json")
72 inputs.file(rootProject.file("yarn.lock"))
73 if (project.hasProperty("ci")) {
74 outputs.file("$buildDir/eslint.json")
75 script.set("run lint:ci")
76 } else {
77 script.set("run lint")
78 }
79 group = "verification" 94 group = "verification"
80 description = "Check for TypeScript lint errors and warnings." 95 description = "Check for TypeScript lint errors and warnings."
81} 96}
@@ -84,11 +99,7 @@ val fixFrontend by tasks.registering(RunYarn::class) {
84 dependsOn(tasks.installFrontend) 99 dependsOn(tasks.installFrontend)
85 dependsOn(generateXStateTypes) 100 dependsOn(generateXStateTypes)
86 dependsOn(typeCheckFrontend) 101 dependsOn(typeCheckFrontend)
87 inputs.dir("src") 102 inputs.files(lintingFiles)
88 inputs.dir("types")
89 inputs.files(".eslintrc.cjs", "prettier.config.cjs")
90 inputs.files("package.json", "tsconfig.json", "tsconfig.base.json", "tsconfig.node.json")
91 inputs.file(rootProject.file("yarn.lock"))
92 script.set("run lint:fix") 103 script.set("run lint:fix")
93 group = "verification" 104 group = "verification"
94 description = "Fix TypeScript lint errors and warnings." 105 description = "Fix TypeScript lint errors and warnings."
@@ -102,11 +113,7 @@ tasks.check {
102tasks.register("serveFrontend", RunYarn::class) { 113tasks.register("serveFrontend", RunYarn::class) {
103 dependsOn(tasks.installFrontend) 114 dependsOn(tasks.installFrontend)
104 dependsOn(generateXStateTypes) 115 dependsOn(generateXStateTypes)
105 inputs.dir("public") 116 inputs.files(assembleFiles)
106 inputs.files(sourcesWithoutTypeGen)
107 inputs.file("index.html")
108 inputs.files("package.json", "tsconfig.json", "tsconfig.base.json", "vite.config.ts")
109 inputs.file(rootProject.file("yarn.lock"))
110 outputs.dir("$viteOutputDir/development") 117 outputs.dir("$viteOutputDir/development")
111 script.set("run serve") 118 script.set("run serve")
112 group = "run" 119 group = "run"
diff --git a/subprojects/frontend/config/eslintReport.cjs b/subprojects/frontend/config/eslintReport.cjs
new file mode 100644
index 00000000..5bf6a041
--- /dev/null
+++ b/subprojects/frontend/config/eslintReport.cjs
@@ -0,0 +1,52 @@
1const { writeFile } = require('node:fs/promises');
2const path = require('node:path');
3const { Readable } = require('node:stream');
4const { pipeline } = require('node:stream/promises');
5
6const { ESLint } = require('eslint');
7
8const rootDir = path.join(__dirname, '..');
9
10/**
11 * Write ESLint report to console.
12 *
13 * @param cli {import('eslint').ESLint} The ESLint CLI.
14 * @param report {import('eslint').ESLint.LintResult[]} The ESLint report.
15 * @return {Promise<void>} A promise that resolves when the report is finished.
16 */
17async function reportToConsole(cli, report) {
18 const stylishFormatter = await cli.loadFormatter('stylish');
19 const output = new Readable();
20 output.push(await stylishFormatter.format(report));
21 output.push(null);
22 return pipeline(output, process.stdout);
23}
24
25/**
26 * Write ESLint report to the <code>build</code> directory.
27 *
28 * @param cli {import('eslint').ESLint} The ESLint CLI.
29 * @param report {import('eslint').ESLint.LintResult[]} The ESLint report.
30 * @return {Promise<void>} A promise that resolves when the report is finished.
31 */
32async function reportToJson(cli, report) {
33 const jsonFormatter = await cli.loadFormatter('json');
34 const json = await jsonFormatter.format(report);
35 const reportPath = path.join(rootDir, 'build', 'eslint.json');
36 return writeFile(reportPath, json, 'utf-8');
37}
38
39async function createReport() {
40 const cli = new ESLint({
41 useEslintrc: true,
42 cwd: rootDir,
43 });
44 const report = await cli.lintFiles('.');
45 await Promise.all([reportToConsole(cli, report), reportToJson(cli, report)]);
46
47 if (report.some((entry) => entry.errorCount > 0)) {
48 process.exitCode = 1;
49 }
50}
51
52createReport().catch(console.error);
diff --git a/subprojects/frontend/package.json b/subprojects/frontend/package.json
index 8b4a3f71..e6bcc89e 100644
--- a/subprojects/frontend/package.json
+++ b/subprojects/frontend/package.json
@@ -9,8 +9,7 @@
9 "serve": "cross-env MODE=development vite serve", 9 "serve": "cross-env MODE=development vite serve",
10 "typegen": "xstate typegen \"src/**/*.ts?(x)\"", 10 "typegen": "xstate typegen \"src/**/*.ts?(x)\"",
11 "typecheck": "tsc -p tsconfig.shared.json && tsc -p tsconfig.node.json && tsc -p tsconfig.json", 11 "typecheck": "tsc -p tsconfig.shared.json && tsc -p tsconfig.node.json && tsc -p tsconfig.json",
12 "lint": "eslint .", 12 "lint": "node config/eslintReport.cjs",
13 "lint:ci": "eslint -f json -o build/eslint.json .",
14 "lint:fix": "yarn run lint --fix" 13 "lint:fix": "yarn run lint --fix"
15 }, 14 },
16 "repository": { 15 "repository": {
@@ -60,7 +59,6 @@
60 }, 59 },
61 "devDependencies": { 60 "devDependencies": {
62 "@lezer/generator": "^1.2.2", 61 "@lezer/generator": "^1.2.2",
63 "@tsconfig/strictest": "^2.0.0",
64 "@types/eslint": "^8.37.0", 62 "@types/eslint": "^8.37.0",
65 "@types/html-minifier-terser": "^7.0.0", 63 "@types/html-minifier-terser": "^7.0.0",
66 "@types/lodash-es": "^4.17.7", 64 "@types/lodash-es": "^4.17.7",
diff --git a/subprojects/frontend/tsconfig.base.json b/subprojects/frontend/tsconfig.base.json
index b960e93c..30e707ae 100644
--- a/subprojects/frontend/tsconfig.base.json
+++ b/subprojects/frontend/tsconfig.base.json
@@ -1,10 +1,28 @@
1{ 1{
2 "extends": "@tsconfig/strictest",
3 "compilerOptions": { 2 "compilerOptions": {
4 "useDefineForClassFields": true, 3 "strict": true,
4 "allowUnusedLabels": false,
5 "allowUnreachableCode": false,
6 "exactOptionalPropertyTypes": true,
7 "noFallthroughCasesInSwitch": true,
8 "noImplicitOverride": true,
9 "noImplicitReturns": true,
10 "noPropertyAccessFromIndexSignature": true,
11 "noUncheckedIndexedAccess": true,
12 "noUnusedLocals": true,
13 "noUnusedParameters": true,
5 "verbatimModuleSyntax": false, 14 "verbatimModuleSyntax": false,
6 "isolatedModules": true, 15 "isolatedModules": true,
16 "checkJs": true,
17 "esModuleInterop": true,
18 "skipLibCheck": true,
19 "forceConsistentCasingInFileNames": true,
20 "useDefineForClassFields": true,
7 "module": "es2022", 21 "module": "es2022",
8 "moduleResolution": "node" 22 "moduleResolution": "node",
23 "incremental": true,
24 "declaration": true,
25 "emitDeclarationOnly": true,
26 "outDir": "build/typescript"
9 } 27 }
10} 28}
diff --git a/subprojects/frontend/tsconfig.json b/subprojects/frontend/tsconfig.json
index 35abd789..35d0d164 100644
--- a/subprojects/frontend/tsconfig.json
+++ b/subprojects/frontend/tsconfig.json
@@ -2,7 +2,6 @@
2 "extends": "./tsconfig.base.json", 2 "extends": "./tsconfig.base.json",
3 "compilerOptions": { 3 "compilerOptions": {
4 "jsx": "react-jsx", 4 "jsx": "react-jsx",
5 "noEmit": true,
6 "lib": ["DOM", "DOM.Iterable", "ES2022"], 5 "lib": ["DOM", "DOM.Iterable", "ES2022"],
7 "types": ["vite/client", "vite-plugin-pwa/client"] 6 "types": ["vite/client", "vite-plugin-pwa/client"]
8 }, 7 },
diff --git a/subprojects/frontend/tsconfig.node.json b/subprojects/frontend/tsconfig.node.json
index f4908bcb..cfa2da13 100644
--- a/subprojects/frontend/tsconfig.node.json
+++ b/subprojects/frontend/tsconfig.node.json
@@ -10,6 +10,7 @@
10 "include": [ 10 "include": [
11 ".eslintrc.cjs", 11 ".eslintrc.cjs",
12 "config/*.ts", 12 "config/*.ts",
13 "config/*.cjs",
13 "prettier.config.cjs", 14 "prettier.config.cjs",
14 "types/node", 15 "types/node",
15 "vite.config.ts" 16 "vite.config.ts"
diff --git a/subprojects/frontend/tsconfig.shared.json b/subprojects/frontend/tsconfig.shared.json
index b7e1de55..f7b56a1d 100644
--- a/subprojects/frontend/tsconfig.shared.json
+++ b/subprojects/frontend/tsconfig.shared.json
@@ -4,8 +4,6 @@
4 "composite": true, 4 "composite": true,
5 "lib": ["ES2022"], 5 "lib": ["ES2022"],
6 "types": [], 6 "types": [],
7 "emitDeclarationOnly": true,
8 "outDir": "build/typescript"
9 }, 7 },
10 "include": [ 8 "include": [
11 "src/xtext/BackendConfig.ts", 9 "src/xtext/BackendConfig.ts",
diff --git a/yarn.lock b/yarn.lock
index cdc9924a..d2aa900f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2112,7 +2112,6 @@ __metadata:
2112 "@material-icons/svg": "npm:^1.0.33" 2112 "@material-icons/svg": "npm:^1.0.33"
2113 "@mui/icons-material": "npm:5.11.16" 2113 "@mui/icons-material": "npm:5.11.16"
2114 "@mui/material": "npm:5.11.16" 2114 "@mui/material": "npm:5.11.16"
2115 "@tsconfig/strictest": "npm:^2.0.0"
2116 "@types/eslint": "npm:^8.37.0" 2115 "@types/eslint": "npm:^8.37.0"
2117 "@types/html-minifier-terser": "npm:^7.0.0" 2116 "@types/html-minifier-terser": "npm:^7.0.0"
2118 "@types/lodash-es": "npm:^4.17.7" 2117 "@types/lodash-es": "npm:^4.17.7"
@@ -2389,13 +2388,6 @@ __metadata:
2389 languageName: node 2388 languageName: node
2390 linkType: hard 2389 linkType: hard
2391 2390
2392"@tsconfig/strictest@npm:^2.0.0":
2393 version: 2.0.0
2394 resolution: "@tsconfig/strictest@npm:2.0.0"
2395 checksum: d3a0f23cc1b0bdfd3d4470dec51e83cb5750c4b867f3e4d74f6900160497d256a291cbc94f700fcb0cf77444618490f637b69d5eeeebe149c6164691f27dc3cd
2396 languageName: node
2397 linkType: hard
2398
2399"@types/braces@npm:*": 2391"@types/braces@npm:*":
2400 version: 3.0.1 2392 version: 3.0.1
2401 resolution: "@types/braces@npm:3.0.1" 2393 resolution: "@types/braces@npm:3.0.1"