Skip to content

Commit

Permalink
Rewrite the Tailwind classes replacement & imports replacement engine…
Browse files Browse the repository at this point in the history
…s, update configs (#37)
  • Loading branch information
WarningImHack3r authored May 18, 2024
1 parent 3cf8340 commit bb51d83
Show file tree
Hide file tree
Showing 78 changed files with 1,756 additions and 393 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@

## [Unreleased]

### Changed

- Entirely rewrite the Tailwind classes replacement engine to be more accurate and faster
- Consequently, the plugin now depends
on [Svelte](https://plugins.jetbrains.com/plugin/12375-svelte) & [Vue](https://plugins.jetbrains.com/plugin/9442-vue-js)
extensions and only works on WebStorm or IntelliJ IDEA Ultimate

## [0.7.7] - 2024-03-29

### Changed
Expand Down
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
[![Version](https://img.shields.io/jetbrains/plugin/v/com.github.warningimhack3r.intellijshadcnplugin.svg)](https://plugins.jetbrains.com/plugin/com.github.warningimhack3r.intellijshadcnplugin)
[![Downloads](https://img.shields.io/jetbrains/plugin/d/com.github.warningimhack3r.intellijshadcnplugin.svg)](https://plugins.jetbrains.com/plugin/com.github.warningimhack3r.intellijshadcnplugin)

## ToDo list before 1.0.0
## 1.0.0 roadmap

- Rework `class`es replacement detection mechanism to be 100% accurate
- Add tests for this
- Add support for Vue `typescript` option (transpiling TypeScript to JavaScript as well as in `*.vue` files)
- See https://github.com/radix-vue/shadcn-vue/issues/378

## Description

<!-- Plugin description -->
Manage your shadcn/ui components in your project. Supports Svelte, React, Vue, and Solid.

This plugin will help you manage your shadcn/ui components through a simple tool window. Add, remove, update them with a single click.
**This plugin will only work with an existing `components.json` file. Manually copied components will not be detected otherwise.**
This plugin will help you manage your shadcn/ui components through a simple tool window. Add, remove, update them with a
single click.
**This plugin will only work with an existing `components.json` file. Manually copied components will not be detected
otherwise.**

## Features

Expand All @@ -33,7 +34,8 @@ This plugin will help you manage your shadcn/ui components through a simple tool

Simply open the `shadcn/ui` tool window and start managing your components.
If you don't see the tool window, you can open it from `View > Tool Windows > shadcn/ui`.
**When adding or removing components, the tool window won't refresh automatically yet. You can refresh it by closing and reopening it.**
**When adding or removing components, the tool window won't refresh automatically yet. You can refresh it by closing and
reopening it.**

## Planned Features

Expand All @@ -44,21 +46,23 @@ If you don't see the tool window, you can open it from `View > Tool Windows > sh
- Figure out a clean way to refresh the tool window automatically after adding or removing components
- Refresh/recreate the tool window automatically when the project finishes indexing
- Add support for monorepos

<!-- Plugin description end -->

## Installation

- Using the IDE built-in plugin system:

<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for "intellij-shadcn-plugin"</kbd> >

<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for "
intellij-shadcn-plugin"</kbd> >
<kbd>Install</kbd>

- Manually:

Download the [latest release](https://github.com/WarningImHack3r/intellij-shadcn-plugin/releases/latest) and install it manually using
Download the [latest release](https://github.com/WarningImHack3r/intellij-shadcn-plugin/releases/latest) and install
it manually using
<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Install plugin from disk...</kbd>


---
Plugin based on the [IntelliJ Platform Plugin Template][template].

Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ pluginGroup = com.github.warningimhack3r.intellijshadcnplugin
pluginName = intellij-shadcn-plugin
pluginRepositoryUrl = https://github.com/WarningImHack3r/intellij-shadcn-plugin
# SemVer format -> https://semver.org
pluginVersion = 0.7.7
pluginVersion = 0.8.0

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 213
pluginUntilBuild =

# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
platformType = IC
platformType = IU
platformVersion = 2021.3

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
platformPlugins =
platformPlugins = JavaScript, dev.blachut.svelte.lang:0.21.1, org.jetbrains.plugins.vue:213.5744.223

# Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion = 8.6
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.github.warningimhack3r.intellijshadcnplugin.backend.helpers

import com.github.warningimhack3r.intellijshadcnplugin.notifications.NotificationManager
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import kotlinx.serialization.json.Json
Expand All @@ -22,9 +21,7 @@ class DependencyManager(private val project: Project) {
"yarn.lock" to "yarn",
"bun.lockb" to "bun"
).filter {
runReadAction {
fileManager.getVirtualFilesByName(it.key).isNotEmpty()
}
fileManager.getVirtualFilesByName(it.key).isNotEmpty()
}.values.firstOrNull()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.warningimhack3r.intellijshadcnplugin.backend.helpers

import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
Expand All @@ -20,16 +22,18 @@ class FileManager(private val project: Project) {
path.substringAfter(deepestRelativePath).split('/').filterNot { it.isEmpty() }.also {
log.debug("Creating subdirectories ${it.joinToString(", ")}")
}.forEach { subdirectory ->
deepest = deepest.createChildDirectory(this, subdirectory)
deepest = runWriteAction { deepest.createChildDirectory(this, subdirectory) }
}
deepest.createChildData(this, file.name).setBinaryContent(file.text.toByteArray()).also {
runWriteAction {
deepest.createChildData(this, file.name).setBinaryContent(file.text.toByteArray())
}.also {
log.debug("Saved file ${file.name} under ${deepest.path}")
}
}

fun deleteFile(file: VirtualFile): Boolean {
return try {
file.delete(this).let { true }
runWriteAction { file.delete(this) }.let { true }
} catch (e: IOException) {
false
}.also {
Expand All @@ -51,10 +55,12 @@ class FileManager(private val project: Project) {
// a simple call to FilenameIndex.getVirtualFilesByName.
// This is a dirty workaround to make it work on production,
// because it works fine during local development.
FilenameIndex.getVirtualFilesByName(
"components.json",
GlobalSearchScope.projectScope(project)
).firstOrNull().also {
runReadAction {
FilenameIndex.getVirtualFilesByName(
"components.json",
GlobalSearchScope.projectScope(project)
)
}.firstOrNull().also {
if (it == null) {
log.warn("components.json not found with the workaround")
}
Expand All @@ -64,10 +70,12 @@ class FileManager(private val project: Project) {
log.warn("No file named $name found with the workaround")
}
} else {
FilenameIndex.getVirtualFilesByName(
name,
GlobalSearchScope.projectScope(project)
)
runReadAction {
FilenameIndex.getVirtualFilesByName(
name,
GlobalSearchScope.projectScope(project)
)
}
}).filter { file ->
!file.path.contains("/node_modules/") && !file.path.contains("/.git/")
}.sortedBy { file ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.github.warningimhack3r.intellijshadcnplugin.backend.helpers

import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory

object PsiHelper {

fun createPsiFile(project: Project, fileName: String, text: String): PsiFile {
assert(fileName.contains('.')) { "File name must contain an extension" }
return runReadAction {
PsiFileFactory.getInstance(project).createFileFromText(
fileName, FileTypeManager.getInstance().getFileTypeByExtension(
fileName.substringAfterLast('.')
), text
)
}
}

fun writeAction(file: PsiFile, description: String? = null, action: () -> Unit) {
WriteCommandAction.runWriteCommandAction(
file.project, description,
"com.github.warningimhack3r.intellijshadcnplugin",
action, file
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ package com.github.warningimhack3r.intellijshadcnplugin.backend.sources

import com.github.warningimhack3r.intellijshadcnplugin.backend.helpers.DependencyManager
import com.github.warningimhack3r.intellijshadcnplugin.backend.helpers.FileManager
import com.github.warningimhack3r.intellijshadcnplugin.backend.helpers.PsiHelper
import com.github.warningimhack3r.intellijshadcnplugin.backend.http.RequestSender
import com.github.warningimhack3r.intellijshadcnplugin.backend.sources.config.Config
import com.github.warningimhack3r.intellijshadcnplugin.backend.sources.remote.Component
import com.github.warningimhack3r.intellijshadcnplugin.backend.sources.remote.ComponentWithContents
import com.github.warningimhack3r.intellijshadcnplugin.notifications.NotificationManager
import com.intellij.notification.NotificationAction
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiFile
import kotlinx.serialization.KSerializer
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -50,13 +50,9 @@ abstract class Source<C : Config>(val project: Project, private val serializer:

protected abstract fun resolveAlias(alias: String): String

protected fun cleanAlias(alias: String): String = if (alias.startsWith("\$")) {
"\\$alias" // fixes Kotlin silently crashing when the replacement starts with $ with a regex
} else alias

protected open fun adaptFileExtensionToConfig(extension: String): String = extension

protected abstract fun adaptFileToConfig(contents: String): String
protected abstract fun adaptFileToConfig(file: PsiFile)

protected open fun fetchComponent(componentName: String): ComponentWithContents {
return RequestSender.sendRequest("$domain/registry/styles/${getLocalConfig().style}/$componentName.json")
Expand Down Expand Up @@ -101,6 +97,7 @@ abstract class Source<C : Config>(val project: Project, private val serializer:
}

open fun addComponent(componentName: String) {
val config = getLocalConfig()
// Install component
val component = fetchComponent(componentName)
val installedComponents = getInstalledComponents()
Expand All @@ -113,7 +110,7 @@ abstract class Source<C : Config>(val project: Project, private val serializer:
log.debug("Installing ${it.size} components: ${it.joinToString(", ") { component -> component.name }}")
}.forEach { downloadedComponent ->
val path =
"${resolveAlias(getLocalConfig().aliases.components)}/${component.type.substringAfterLast(":")}" + if (usesDirectoriesForComponents()) {
"${resolveAlias(config.aliases.components)}/${component.type.substringAfterLast(":")}" + if (usesDirectoriesForComponents()) {
"/${downloadedComponent.name}"
} else ""
// Check for deprecated components
Expand All @@ -134,7 +131,7 @@ abstract class Source<C : Config>(val project: Project, private val serializer:
listOf(
NotificationAction.createSimple("Remove " + if (multipleFiles) "them" else "it") {
remotelyDeletedFiles.forEach { file ->
runWriteAction { fileManager.deleteFile(file) }
fileManager.deleteFile(file)
}
log.info(
"Removed deprecated file${if (multipleFiles) "s" else ""} from ${downloadedComponent.name} (${
Expand All @@ -157,13 +154,10 @@ abstract class Source<C : Config>(val project: Project, private val serializer:
}
}
downloadedComponent.files.forEach { file ->
val psiFile = PsiFileFactory.getInstance(project).createFileFromText(
adaptFileExtensionToConfig(file.name),
FileTypeManager.getInstance().getFileTypeByExtension(
adaptFileExtensionToConfig(file.name).substringAfterLast('.')
),
adaptFileToConfig(file.content)
val psiFile = PsiHelper.createPsiFile(
project, adaptFileExtensionToConfig(file.name), file.content
)
adaptFileToConfig(psiFile)
fileManager.saveFileAtPath(psiFile, path)
}
}
Expand Down Expand Up @@ -205,14 +199,21 @@ abstract class Source<C : Config>(val project: Project, private val serializer:

open fun isComponentUpToDate(componentName: String): Boolean {
val remoteComponent = fetchComponent(componentName)
val componentPath =
"${resolveAlias(getLocalConfig().aliases.components)}/${remoteComponent.type.substringAfterLast(":")}${
if (usesDirectoriesForComponents()) {
"/${remoteComponent.name}"
} else ""
}"
val fileManager = FileManager(project)
return remoteComponent.files.all { file ->
(FileManager(project).getFileContentsAtPath(
"${resolveAlias(getLocalConfig().aliases.components)}/${remoteComponent.type.substringAfterLast(":")}${
if (usesDirectoriesForComponents()) {
"/${remoteComponent.name}"
} else ""
}/${file.name}"
) == adaptFileToConfig(file.content)).also {
val psiFile = PsiHelper.createPsiFile(
project, adaptFileExtensionToConfig(file.name), file.content
)
adaptFileToConfig(psiFile)
(fileManager.getFileContentsAtPath("$componentPath/${file.name}") == runReadAction {
psiFile.text
}).also {
log.debug("File ${file.name} for ${remoteComponent.name} is ${if (it) "" else "NOT "}up to date")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import kotlinx.serialization.Serializable
@Suppress("PROVIDED_RUNTIME_TOO_LOW", "kotlin:S117")
@Serializable
class ReactConfig(
override val `$schema`: String,
override val `$schema`: String = "https://ui.shadcn.com/schema.json",
override val style: String,
override val tailwind: Tailwind,
val rsc: Boolean,
val rsc: Boolean = false,
val tsx: Boolean = true,
override val tailwind: Tailwind,
override val aliases: Aliases
) : Config() {

Expand All @@ -35,7 +35,7 @@ class ReactConfig(
override val config: String,
override val css: String,
override val baseColor: String,
val cssVariables: Boolean,
val cssVariables: Boolean = true,
val prefix: String = ""
) : Config.Tailwind()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
@Suppress("PROVIDED_RUNTIME_TOO_LOW", "kotlin:S117")
@Serializable
class SolidConfig(
override val `$schema`: String,
override val `$schema`: String = "",
override val style: String,
override val tailwind: VueConfig.Tailwind,
override val aliases: Aliases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable
@Suppress("PROVIDED_RUNTIME_TOO_LOW", "kotlin:S117")
@Serializable
class SvelteConfig(
override val `$schema`: String,
override val `$schema`: String = "https://shadcn-svelte.com/schema.json",
override val style: String,
override val tailwind: Tailwind,
val typescript: Boolean = true,
override val aliases: Aliases
override val aliases: Aliases,
val typescript: Boolean = true
) : Config() {

@Serializable
Expand Down
Loading

0 comments on commit bb51d83

Please sign in to comment.