|
| 1 | +import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform |
| 2 | +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension |
| 3 | +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin |
| 4 | +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest |
| 5 | + |
| 6 | +@Suppress("DSL_SCOPE_VIOLATION", "RUN_IN_SCRIPT") // https://github.com/gradle/gradle/issues/22797 |
| 7 | +plugins { |
| 8 | + kotlin("js") |
| 9 | + id("com.saveourtool.save.buildutils.build-frontend-image-configuration") |
| 10 | + id("com.saveourtool.save.buildutils.code-quality-convention") |
| 11 | + id("com.saveourtool.save.buildutils.save-cloud-version-file-configuration") |
| 12 | + alias(libs.plugins.kotlin.plugin.serialization) |
| 13 | +} |
| 14 | + |
| 15 | +rootProject.plugins.withType<NodeJsRootPlugin> { |
| 16 | + rootProject.the<NodeJsRootExtension>().nodeVersion = "16.13.1" |
| 17 | +} |
| 18 | + |
| 19 | +dependencies { |
| 20 | + implementation(projects.saveCloudCommon) |
| 21 | + implementation(projects.saveFrontendCommon) |
| 22 | + |
| 23 | + implementation(enforcedPlatform(libs.kotlin.wrappers.bom)) |
| 24 | + implementation("org.jetbrains.kotlin-wrappers:kotlin-react") |
| 25 | + implementation("org.jetbrains.kotlin-wrappers:kotlin-extensions") |
| 26 | + implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom") |
| 27 | + implementation("org.jetbrains.kotlin-wrappers:kotlin-react-router-dom") |
| 28 | + implementation("org.jetbrains.kotlin-wrappers:kotlin-tanstack-react-table") |
| 29 | + implementation("org.jetbrains.kotlin-wrappers:kotlin-mui-icons") |
| 30 | + implementation("org.jetbrains.kotlin-wrappers:kotlin-mui") |
| 31 | + implementation("io.github.petertrr:kotlin-multiplatform-diff-js:0.4.0") |
| 32 | + |
| 33 | + implementation(libs.save.common) |
| 34 | + implementation(libs.kotlinx.coroutines.core) |
| 35 | + implementation(libs.kotlinx.serialization.json) |
| 36 | + implementation(libs.kotlinx.datetime) |
| 37 | + implementation(libs.ktor.http) |
| 38 | +} |
| 39 | + |
| 40 | +val distributionsDirName = "distributions" |
| 41 | + |
| 42 | +kotlin { |
| 43 | + js(IR) { |
| 44 | + // as for `-pre.148-kotlin-1.4.21`, react-table gives errors with IR |
| 45 | + browser { |
| 46 | + distribution( |
| 47 | + Action { |
| 48 | + // TODO: need to remove this overriding |
| 49 | + outputDirectory = layout.buildDirectory.dir(distributionsDirName) |
| 50 | + } |
| 51 | + ) |
| 52 | + testTask { |
| 53 | + useKarma { |
| 54 | + when (properties["save.profile"]) { |
| 55 | + "dev" -> { |
| 56 | + useChrome() |
| 57 | + // useFirefox() |
| 58 | + } |
| 59 | + null -> useChromeHeadless() |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | + commonWebpackConfig { |
| 64 | + cssSupport { |
| 65 | + enabled.set(true) |
| 66 | + } |
| 67 | + } |
| 68 | + } |
| 69 | + // kotlin-wrapper migrates to commonjs and missed @JsNonModule annotations |
| 70 | + // https://github.com/JetBrains/kotlin-wrappers/issues/1935 |
| 71 | + useCommonJs() |
| 72 | + binaries.executable() // already default for LEGACY, but explicitly needed for IR |
| 73 | + sourceSets.all { |
| 74 | + languageSettings.apply { |
| 75 | + optIn("kotlin.RequiresOptIn") |
| 76 | + optIn("kotlinx.serialization.ExperimentalSerializationApi") |
| 77 | + optIn("kotlin.js.ExperimentalJsExport") |
| 78 | + } |
| 79 | + } |
| 80 | + sourceSets["main"].dependencies { |
| 81 | + compileOnly(devNpm("sass", "^1.43.0")) |
| 82 | + compileOnly(devNpm("sass-loader", "^12.0.0")) |
| 83 | + compileOnly(devNpm("style-loader", "^3.3.1")) |
| 84 | + compileOnly(devNpm("css-loader", "^6.5.0")) |
| 85 | + compileOnly(devNpm("file-loader", "^6.2.0")) |
| 86 | + // https://getbootstrap.com/docs/4.0/getting-started/webpack/#importing-precompiled-sass |
| 87 | + compileOnly(devNpm("postcss-loader", "^6.2.1")) |
| 88 | + compileOnly(devNpm("postcss", "^8.2.13")) |
| 89 | + // See https://stackoverflow.com/a/72828500; newer versions are supported only for Bootstrap 5.2+ |
| 90 | + compileOnly(devNpm("autoprefixer", "10.4.5")) |
| 91 | + compileOnly(devNpm("webpack-bundle-analyzer", "^4.5.0")) |
| 92 | + compileOnly(devNpm("mini-css-extract-plugin", "^2.6.0")) |
| 93 | + compileOnly(devNpm("html-webpack-plugin", "^5.5.0")) |
| 94 | + |
| 95 | + // web-specific dependencies |
| 96 | + implementation(npm("@fortawesome/fontawesome-svg-core", "^1.2.36")) |
| 97 | + implementation(npm("@fortawesome/free-solid-svg-icons", "5.15.3")) |
| 98 | + implementation(npm("@fortawesome/free-brands-svg-icons", "5.15.3")) |
| 99 | + implementation(npm("@fortawesome/react-fontawesome", "^0.1.16")) |
| 100 | + implementation(npm("devicon", "^2.15.1")) |
| 101 | + implementation(npm("animate.css", "^4.1.1")) |
| 102 | + implementation(npm("react-scroll-motion", "^0.3.0")) |
| 103 | + implementation(npm("react-spinners", "0.13.0")) |
| 104 | + implementation(npm("react-tsparticles", "1.42.1")) |
| 105 | + implementation(npm("tsparticles", "2.1.3")) |
| 106 | + implementation(npm("jquery", "3.6.0")) |
| 107 | + // BS5: implementation(npm("@popperjs/core", "2.11.0")) |
| 108 | + implementation(npm("popper.js", "1.16.1")) |
| 109 | + // BS5: implementation(npm("bootstrap", "5.0.1")) |
| 110 | + implementation(npm("react-calendar", "^3.8.0")) |
| 111 | + implementation(npm("bootstrap", "^4.6.0")) |
| 112 | + implementation(npm("react", "^18.0.0")) |
| 113 | + implementation(npm("react-dom", "^18.0.0")) |
| 114 | + implementation(npm("react-modal", "^3.0.0")) |
| 115 | + implementation(npm("os-browserify", "^0.3.0")) |
| 116 | + implementation(npm("path-browserify", "^1.0.1")) |
| 117 | + implementation(npm("react-minimal-pie-chart", "^8.2.0")) |
| 118 | + implementation(npm("lodash.debounce", "^4.0.8")) |
| 119 | + implementation(npm("react-markdown", "^8.0.3")) |
| 120 | + implementation(npm("rehype-highlight", "^5.0.2")) |
| 121 | + implementation(npm("react-ace", "^10.1.0")) |
| 122 | + implementation(npm("react-avatar-image-cropper", "^1.4.2")) |
| 123 | + implementation(npm("react-circle", "^1.1.1")) |
| 124 | + implementation(npm("react-diff-viewer-continued", "^3.2.6")) |
| 125 | + implementation(npm("react-json-view", "^1.21.3")) |
| 126 | + implementation(npm("multi-range-slider-react", "^2.0.5")) |
| 127 | + // react-sigma |
| 128 | + implementation(npm("@react-sigma/core", "^3.1.0")) |
| 129 | + implementation(npm("sigma", "^2.4.0")) |
| 130 | + implementation(npm("graphology", "^0.25.1")) |
| 131 | + implementation(npm("graphology-layout", "^0.6.1")) |
| 132 | + implementation(npm("graphology-layout-forceatlas2", "^0.10.1")) |
| 133 | + implementation(npm("@react-sigma/layout-core", "^3.1.0")) |
| 134 | + implementation(npm("@react-sigma/layout-random", "^3.1.0")) |
| 135 | + implementation(npm("@react-sigma/layout-circular", "^3.1.0")) |
| 136 | + implementation(npm("@react-sigma/layout-forceatlas2", "^3.1.0")) |
| 137 | + implementation(npm("react-graph-viz-engine", "^0.1.0")) |
| 138 | + implementation(npm("cytoscape", "^3.25.0")) |
| 139 | + // translation |
| 140 | + implementation(npm("i18next", "^23.4.5")) |
| 141 | + implementation(npm("react-i18next", "^13.2.0")) |
| 142 | + implementation(npm("i18next-http-backend", "^2.2.2")) |
| 143 | + implementation(npm("js-cookie", "^3.0.5")) |
| 144 | + // transitive dependencies with explicit version ranges required for security reasons |
| 145 | + compileOnly(devNpm("minimist", "^1.2.6")) |
| 146 | + compileOnly(devNpm("async", "^2.6.4")) |
| 147 | + compileOnly(devNpm("follow-redirects", "^1.14.8")) |
| 148 | + } |
| 149 | + sourceSets["test"].dependencies { |
| 150 | + implementation(kotlin("test-js")) |
| 151 | + implementation(devNpm("jsdom", "^19.0.0")) |
| 152 | + implementation(devNpm("global-jsdom", "^8.4.0")) |
| 153 | + implementation(devNpm("@testing-library/react", "^13.2.0")) |
| 154 | + implementation(devNpm("@testing-library/user-event", "^14.0.0")) |
| 155 | + implementation(devNpm("karma-mocha-reporter", "^2.0.0")) |
| 156 | + implementation(devNpm("istanbul-instrumenter-loader", "^3.0.1")) |
| 157 | + implementation(devNpm("karma-coverage-istanbul-reporter", "^3.0.3")) |
| 158 | + implementation(devNpm("msw", "^0.40.0")) |
| 159 | + } |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +rootProject.plugins.withType(NodeJsRootPlugin::class.java) { |
| 164 | + rootProject.the<NodeJsRootExtension>().versions.apply { |
| 165 | + // workaround for continuous work of WebPack: (https://github.com/webpack/webpack-cli/issues/2990) |
| 166 | + webpackCli.version = "4.9.0" |
| 167 | + webpackDevServer.version = "^4.9.0" |
| 168 | + // override default version from KGP for security reasons |
| 169 | + karma.version = "^6.3.14" |
| 170 | + mocha.version = "^9.2.0" |
| 171 | + } |
| 172 | +} |
| 173 | +// store yarn.lock in the root directory |
| 174 | +rootProject.extensions.configure<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension> { |
| 175 | + lockFileDirectory = rootProject.projectDir |
| 176 | +} |
| 177 | + |
| 178 | +val mswScriptTargetPath = file("${rootProject.buildDir}/js/packages/${rootProject.name}-${project.name}-test/node_modules").absolutePath |
| 179 | +val mswScriptTargetFile = "$mswScriptTargetPath/mockServiceWorker.js" |
| 180 | +@Suppress("GENERIC_VARIABLE_WRONG_DECLARATION") |
| 181 | +val installMwsScriptTaskProvider = tasks.register<Exec>("installMswScript") { |
| 182 | + dependsOn(":kotlinNodeJsSetup", ":kotlinNpmInstall", "packageJson") |
| 183 | + inputs.dir(mswScriptTargetPath) |
| 184 | + outputs.file(mswScriptTargetFile) |
| 185 | + // cd to directory where the generated package.json is located. This is required for correct operation of npm/npx |
| 186 | + workingDir("$rootDir/build/js") |
| 187 | + |
| 188 | + val isWindows = DefaultNativePlatform.getCurrentOperatingSystem().isWindows |
| 189 | + val nodeJsEnv = NodeJsRootPlugin.apply(project.rootProject).requireConfigured() |
| 190 | + val nodeDir = nodeJsEnv.nodeDir |
| 191 | + val nodeBinDir = nodeJsEnv.nodeBinDir |
| 192 | + listOf( |
| 193 | + System.getenv("PATH"), |
| 194 | + nodeBinDir.absolutePath, |
| 195 | + ) |
| 196 | + .filterNot { it.isNullOrEmpty() } |
| 197 | + .joinToString(separator = File.pathSeparator) |
| 198 | + .let { environment("PATH", it) } |
| 199 | + |
| 200 | + if (!isWindows) { |
| 201 | + doFirst { |
| 202 | + // workaround, because `npx` is a symlink but symlinks are lost when Gradle unpacks archive |
| 203 | + exec { |
| 204 | + commandLine("ln", "-sf", "$nodeDir/lib/node_modules/npm/bin/npx-cli.js", "$nodeBinDir/npx") |
| 205 | + } |
| 206 | + exec { |
| 207 | + commandLine("ln", "-sf", "$nodeDir/lib/node_modules/npm/bin/npm-cli.js", "$nodeBinDir/npm") |
| 208 | + } |
| 209 | + exec { |
| 210 | + commandLine("ln", "-sf", "$nodeDir/lib/node_modules/corepack/dist/corepack.js", "$nodeBinDir/corepack") |
| 211 | + } |
| 212 | + } |
| 213 | + } |
| 214 | + |
| 215 | + commandLine( |
| 216 | + nodeBinDir.resolve(if (isWindows) "npx.cmd" else "npx").canonicalPath, |
| 217 | + "msw", |
| 218 | + "init", |
| 219 | + mswScriptTargetPath, |
| 220 | + "--no-save", |
| 221 | + ) |
| 222 | +} |
| 223 | +tasks.named<KotlinJsTest>("browserTest").configure { |
| 224 | + dependsOn(installMwsScriptTaskProvider) |
| 225 | + inputs.file(mswScriptTargetFile) |
| 226 | +} |
| 227 | + |
| 228 | +tasks.withType<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack> { |
| 229 | + // Since we inject timestamp into HTML file, we would like this task to always be re-run. |
| 230 | + inputs.property("Build timestamp", System.currentTimeMillis()) |
| 231 | + doFirst { |
| 232 | + val additionalWebpackResources = fileTree("$buildDir/processedResources/js/main/") { |
| 233 | + include("scss/**") |
| 234 | + include("index.html") |
| 235 | + } |
| 236 | + copy { |
| 237 | + from(additionalWebpackResources) |
| 238 | + into("${rootProject.buildDir}/js/packages/${rootProject.name}-${project.name}") |
| 239 | + } |
| 240 | + } |
| 241 | +} |
| 242 | + |
| 243 | +val distribution: Configuration by configurations.creating |
| 244 | +val distributionJarTask by tasks.registering(Jar::class) { |
| 245 | + dependsOn(":save-cosv-frontend:browserDistribution") |
| 246 | + archiveClassifier.set("distribution") |
| 247 | + from("$buildDir/$distributionsDirName") { |
| 248 | + into("static") |
| 249 | + exclude("scss") |
| 250 | + } |
| 251 | + from("$projectDir/nginx.conf") { |
| 252 | + into("") |
| 253 | + } |
| 254 | +} |
| 255 | +artifacts.add(distribution.name, distributionJarTask.get().archiveFile) { |
| 256 | + builtBy(distributionJarTask) |
| 257 | +} |
| 258 | + |
| 259 | +detekt { |
| 260 | + config.setFrom(config.plus(file("detekt.yml"))) |
| 261 | +} |
0 commit comments