fun apply()

in libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/web/nodejs/NodeJsRootPluginApplier.kt [48:324]


    fun apply(project: Project) {
        MultiplePluginDeclarationDetector.detect(project)

        check(project == project.rootProject) {
            "${this::class.java.name} can be applied only to root project"
        }

        project.plugins.apply(BasePlugin::class.java)

        val nodeJsRoot = project.extensions.create(
            nodeJsRootName,
            nodeJsRootKlass.java,
            project,
            { singleNodeJsPluginApply(project) },
            rootDirectoryName,
        )

        val npm = project.extensions.create(
            npmName,
            npmKlass.java,
            project,
            nodeJsRoot,
        )

        val nodeJs = singleNodeJsPluginApply(project)

        npm.nodeJsEnvironment.value(
            nodeJs.env
        ).disallowChanges()

        nodeJsRoot.packageManagerExtension.convention(
            npm
        )

        npm.lockFileDirectory.convention(
            lockFileDirectory(project.layout.projectDirectory)
        )

        val gradleNodeModulesProvider: Provider<GradleNodeModulesCache> = GradleNodeModulesCache.registerIfAbsent(
            project,
            project.projectDir,
            nodeJsRoot.nodeModulesGradleCacheDirectory,
            platformDisambiguate::extensionName
        )

        val setupFileHasherTask =
            project.registerTask<KotlinNpmCachesSetup>(platformDisambiguate.extensionName(KotlinNpmCachesSetup.NAME)) {
                it.description = "Setup file hasher for caches"

                it.gradleNodeModules.set(gradleNodeModulesProvider)
            }

        project.registerTask<Task>(platformDisambiguate.extensionName(PACKAGE_JSON_UMBRELLA_TASK_NAME))

        nodeJsRoot.resolver = KotlinRootNpmResolver(
            project.name,
            project.version.toString(),
            TasksRequirements(),
            nodeJsRoot.versions,
            nodeJsRoot.rootProjectDir,
            platformType
        )

        val objectFactory = project.objects

        val npmResolutionManager: Provider<KotlinNpmResolutionManager> = KotlinNpmResolutionManager.registerIfAbsent(
            project,
            objectFactory.providerWithLazyConvention {
                nodeJsRoot.resolver.close()
            },
            gradleNodeModulesProvider,
            nodeJsRoot.projectPackagesDirectory
        ) {
            platformDisambiguate.extensionName(it, prefix = null)
        }

        val packageJsonUmbrella = nodeJsRoot
            .packageJsonUmbrellaTaskProvider

        val rootPackageJson =
            project.tasks.register(
                platformDisambiguate.extensionName(
                    RootPackageJsonTask.NAME,
                    prefix = null,
                ),
                RootPackageJsonTask::class.java
            ) { task ->
                task.group = NodeJsRootPlugin.TASKS_GROUP_NAME
                task.description = "Create root package.json"

                task.configureNodeJsEnvironmentWithNpmResolutionManagerTasks(
                    setupFileHasherTask,
                    nodeJsRoot,
                    nodeJs,
                    npmResolutionManager,
                )

                task.rootPackageJsonFile.value(
                    nodeJsRoot.rootPackageDirectory.map { it.file(NpmProject.PACKAGE_JSON) }
                ).disallowChanges()

                task.onlyIf("Prepare NPM project only in configuring state") {
                    it as RootPackageJsonTask
                    it.npmResolutionManager.get().isConfiguringState()
                }

                task.dependsOn(packageJsonUmbrella)
            }

        configureRequiresNpmDependencies(project, rootPackageJson)

        val npmInstall =
            project.registerTask<KotlinNpmInstallTask>(platformDisambiguate.extensionName(KotlinNpmInstallTask.BASE_NAME)) { npmInstall ->
                with(nodeJs) {
                    npmInstall.dependsOn(project.nodeJsSetupTaskProvider)
                }
                npmInstall.group = NodeJsRootPlugin.TASKS_GROUP_NAME
                npmInstall.description = "Find, download and link NPM dependencies and projects"

                npmInstall.configureNodeJsEnvironmentWithNpmResolutionManagerTasks(
                    setupFileHasherTask,
                    nodeJsRoot,
                    nodeJs,
                    npmResolutionManager,
                )

                npmInstall.nodeModules.value(
                    nodeJsRoot.rootPackageDirectory.map { it.dir("node_modules") }
                ).disallowChanges()

                npmInstall.additionalFiles.from(
                    nodeJsRoot.packageManagerExtension.map { it.additionalInstallOutput }
                ).disallowChanges()

                npmInstall.preparedFiles.from(
                    nodeJsRoot.packageManagerExtension.zip(npmInstall.nodeJsEnvironment) { npmApiExt, nodeJsEnvironment ->
                        npmApiExt.packageManager.preparedFiles(nodeJsEnvironment)
                    }
                ).disallowChanges()

                npmInstall.onlyIf("No package.json files for install") { task ->
                    task as KotlinNpmInstallTask
                    task.preparedFiles.all { file ->
                        file.exists()
                    }
                }

                npmInstall.outputs.upToDateWhen {
                    npmInstall.nodeModules.getFile().exists()
                }

                npmInstall.dependsOn(rootPackageJson)
                npmInstall.inputs.property("npmIgnoreScripts", { npm.ignoreScripts })

                npmInstall.dependsOn(nodeJsRoot.packageManagerExtension.map { it.preInstallTasks })
            }

        val upgradeLock = project.tasks.register(
            platformDisambiguate.extensionName(LockCopyTask.UPGRADE_PACKAGE_LOCK_BASE_NAME),
            LockStoreTask::class.java
        ) { task ->
            task.dependsOn(npmInstall)
            task.inputFile.set(nodeJsRoot.rootPackageDirectory.map { it.file(LockCopyTask.PACKAGE_LOCK) })
            task.outputDirectory.set(npm.lockFileDirectory)
            task.fileName.set(npm.lockFileName)

            task.additionalInputFiles.from(
                nodeJsRoot.rootPackageDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
            )
            task.additionalInputFiles.from(
                task.outputDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
            )

            task.lockFileMismatchReport.value(
                LockFileMismatchReport.NONE
            ).disallowChanges()
            task.reportNewLockFile.value(
                false
            ).disallowChanges()
            task.lockFileAutoReplace.value(
                true
            ).disallowChanges()
        }

        project.tasks.register(
            platformDisambiguate.extensionName(LockCopyTask.STORE_PACKAGE_LOCK_BASE_NAME),
            LockStoreTask::class.java
        ) { task ->
            task.dependsOn(npmInstall)
            task.inputFile.set(nodeJsRoot.rootPackageDirectory.map { it.file(LockCopyTask.PACKAGE_LOCK) })

            task.additionalInputFiles.from(
                nodeJsRoot.rootPackageDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
            )
            task.additionalInputFiles.from(
                task.outputDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
            )

            task.outputDirectory.set(npm.lockFileDirectory)
            task.fileName.set(npm.lockFileName)

            task.lockFileMismatchReport.value(
                project.provider { npm.requireConfigured().packageLockMismatchReport }
            ).disallowChanges()
            task.reportNewLockFile.value(
                project.provider { npm.requireConfigured().reportNewPackageLock }
            ).disallowChanges()
            task.lockFileAutoReplace.value(
                project.provider { npm.requireConfigured().packageLockAutoReplace }
            ).disallowChanges()
            task.mismatchMessage.value(
                LockCopyTask.packageLockMismatchMessage(
                    upgradeLock.name
                )
            )
        }

        project.tasks.register(
            platformDisambiguate.extensionName(LockCopyTask.RESTORE_PACKAGE_LOCK_BASE_NAME),
            LockCopyTask::class.java
        ) { task ->
            task.inputFile.set(
                npm.lockFileDirectory.flatMap { dir ->
                    dir.file(npm.lockFileName)
                }
            )
            task.additionalInputFiles.from(
                npm.lockFileDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
            )
            task.outputDirectory.set(nodeJsRoot.rootPackageDirectory)
            task.fileName.set(LockCopyTask.PACKAGE_LOCK)
            task.onlyIf {
                val inputFileExists = task.inputFile.getOrNull()?.asFile?.exists() == true
                // Workaround for "skip if not exists"
                // https://github.com/gradle/gradle/issues/2919
                if (!inputFileExists) {
                    task.inputFile.set(null as RegularFile?)
                }
                inputFileExists || task.additionalInputFiles.files.any { it.exists() }
            }
        }

        npm.preInstallTasks.value(
            listOf(npm.restorePackageLockTaskProvider)
        ).disallowChanges()

        npm.postInstallTasks.value(
            listOf(npm.storePackageLockTaskProvider)
        ).disallowChanges()

        @Suppress("DEPRECATION")
        project.tasks.register(
            platformDisambiguate.extensionName(
                "node" + org.jetbrains.kotlin.gradle.tasks.CleanDataTask.NAME_SUFFIX,
                prefix = null,
            ),
            org.jetbrains.kotlin.gradle.tasks.CleanDataTask::class.java
        ) {
            it.doFirst {
                it.logger.warn(org.jetbrains.kotlin.gradle.tasks.CleanDataTask.Companion.deprecationMessage(it.path))
            }

            it.cleanableStoreProvider = nodeJs
                .installationDirectory
                .map { org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore.Companion[it.asFile.path] }
            it.group = NodeJsRootPlugin.TASKS_GROUP_NAME
            it.description = "Clean unused local node version"
        }

        beforePackageManager(project)

        val propertiesProvider = PropertiesProvider.Companion(project)

        if (propertiesProvider.yarn) {
            project.plugins.apply(yarnPlugin.java)
        }
    }