Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR [Feature]: Use Tailwind's built-in Plugin API and configure Intellisense. #93

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 123 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { type IDisposable, type languages, type MonacoEditor } from 'monaco-types'
import { type Config } from 'tailwindcss'
import { PluginAPI } from 'tailwindcss/types/config.js'

/**
* A Tailwind configuration, but without content.
* A Tailwind configuration, where all properties are optional.
*/
export type TailwindConfig = Omit<Config, 'content'>
export type TailwindConfig = Partial<Config>

/**
* An object of the arguments for the Tailwind's built-in Plugin API.
*/
export type TailwindPluginAPI = Partial<{
[T in keyof PluginAPI]: Parameters<PluginAPI[T]>
}>

type DiagnosticSeveritySetting = 'ignore' | 'warning' | 'error'

export interface MonacoTailwindcssOptions {
/**
Expand All @@ -19,6 +29,117 @@ export interface MonacoTailwindcssOptions {
* worker.
*/
tailwindConfig?: TailwindConfig | string
/**
* Extend Intellisense's settings.
*
* Default values are based on the VS Code extension.
*
* ``` typescript
* const defaultSettings = {
editor: {tabSize: 2},
tailwindCSS: {
emmetCompletions: false,
classAttributes: ["class", "className", "ngClass"],
codeActions: true,
hovers: true,
suggestions: true,
validate: true,
colorDecorators: true,
rootFontSize: 16,
showPixelEquivalents: true,
includeLanguages: {},
files: {exclude: []},
experimental: {
classRegex: [],
configFile: {}
},
lint: {
cssConflict: "warning",
invalidApply: "error",
invalidScreen: "error",
invalidVariant: "error",
invalidConfigPath: "error",
invalidTailwindDirective: "error",
recommendedVariantOrder: "warning"
},
}
}
*/
intellisense?: Partial<{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VSCode calls this settings. LSP calls it configuration. Since we already have so many things called configuration, I’m leaning towards calling this property settings.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

editor: {
tabSize: number
}
tailwindCSS: Partial<{
emmetCompletions: boolean
includeLanguages: Record<string, string>
classAttributes: string[]
suggestions: boolean
hovers: boolean
codeActions: boolean
validate: boolean
showPixelEquivalents: boolean
rootFontSize: number
colorDecorators: boolean
files: { exclude: string[] }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this property is meaningless to the Monaco implementation? Let’s omit it. Also consider for each property what it does, and whether it makes sense to define it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I frankly don't know which properties are supported in Monaco or even what they do. I just guessed that as they're present (even if empty) in the default settings used, I might as well give access to them. Maybe should we accept only the one that probably matters the most, classAttributes?

experimental: {
classRegex: string[]
configFile: string | Record<string, string | string[]>
}
lint: {
cssConflict: DiagnosticSeveritySetting
invalidApply: DiagnosticSeveritySetting
invalidScreen: DiagnosticSeveritySetting
invalidVariant: DiagnosticSeveritySetting
invalidConfigPath: DiagnosticSeveritySetting
invalidTailwindDirective: DiagnosticSeveritySetting
recommendedVariantOrder: DiagnosticSeveritySetting
}
}>
}>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all of these nested properties should be optional, also the deeply nested ones. Since we’re defining the object, use the optional property symbol ?, not the Partial type.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted

/**
* A way to call Tailwind's built-in Plugin API within the worker.
* ``` typescript
*
// Instead of calling the functions inside your config like this...
* const tailwindConfig = {
// ...config
* plugins: [
({addUtilities}) => {
addUtilities({
'.custom-class': {
color: '#000000',
fontSize: '1rem',
fontWeight: 900
}
})
}
]
* }

// ...you provide an array containing the arguments
// for each function you wish to call.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is your interpretation of how the configuration could work. This might cater to your specific situation, but it might not to others’. The prepareTailwindConfig option from initialize allows you to fully customize how the Tailwind config provided could be interpreted.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, this whole thing was a big overlook, might call it a skill issue, on my side... Remember in the issue that led to this PR when I said I tried initialize but didn't make it work and couldn't really understand what is its purpose?

I was calling that thing just above window.MonacoEnvironment where I create the different workers... This is also my first time using workers, I didn't know better. Now, I've created a file for the worker, use initialize in it and point the tailwindcss worker to that file. And it just works...


// Note that the arguments you pass must be serializable,
// e.g they can't include functions.

* const options = {
tailwindConfig: {...},
intellisense: {...},
* pluginAPI: {
* addUtilities: [
* {
* '.custom-class': {
* color: '#000000',
* fontSize: '1rem',
* fontWeight: 900
* }
* }
* ]
* }
* }
* ```
*/
pluginAPI?: TailwindPluginAPI
}

/**
Expand Down
53 changes: 49 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 20 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "monaco-tailwindcss",
"version": "0.6.1",
"name": "monaco-tailwind",
"version": "0.1.0",
"description": "Tailwindcss integration for Monaco editor",
"files": [
"index.js",
Expand All @@ -23,7 +23,10 @@
"./tailwindcss.worker": "./tailwindcss.worker.js",
"./tailwindcss.worker.js": "./tailwindcss.worker.js"
},
"repository": "remcohaszing/monaco-tailwindcss",
"repository": {
"type": "git",
"url": "git+https://github.com/remcohaszing/monaco-tailwindcss.git"
},
"keywords": [
"monaco",
"monaco-editor",
Expand All @@ -32,15 +35,19 @@
],
"author": "Remco Haszing <[email protected]>",
"license": "MIT",
"bugs": "https://github.com/remcohaszing/monaco-tailwindcss/issues",
"bugs": {
"url": "https://github.com/remcohaszing/monaco-tailwindcss/issues"
},
"homepage": "https://monaco-tailwindcss.js.org",
"funding": "https://github.com/sponsors/remcohaszing",
"funding": {
"url": "https://github.com/sponsors/remcohaszing"
},
"dependencies": {
"@alloc/quick-lru": "^5.0.0",
"@ctrl/tinycolor": "^3.0.0",
"@csstools/css-parser-algorithms": "^2.0.0",
"@csstools/css-tokenizer": "^2.0.0",
"@csstools/media-query-list-parser": "^2.0.0",
"@ctrl/tinycolor": "^3.0.0",
"color-name": "^2.0.0",
"css.escape": "^1.0.0",
"culori": "^4.0.0",
Expand All @@ -49,6 +56,7 @@
"line-column": "^1.0.0",
"monaco-languageserver-types": "^0.4.0",
"monaco-marker-data-provider": "^1.0.0",
"monaco-tailwind": "^0.0.1",
"monaco-types": "^0.1.0",
"monaco-worker-manager": "^2.0.0",
"moo": "^0.5.0",
Expand Down Expand Up @@ -82,5 +90,10 @@
"@tailwindcss/language-service": {
"postcss": "^8.0.0"
}
}
},
"main": "index.js",
"directories": {
"example": "examples"
},
"types": "./index.d.ts"
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What’s with all these metadata changes? None of the changes here are needed. I also noticed you published your fork as monaco-tailwind and didn’t update any of the metadata? Forking is fine, publishing too, but using such a similar name to publish a fork doesn’t seem like the nices thing to do. It’s also misleading to both your and my users to point your metadata at my project.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, as I said, this is my very first time contributing, doing PR. I've published a package to test it on the project where I use Monaco. I only later discovered that I could have tested it directly with the demo in the repo. My bad on that. As I knew I would eventually delete the package, I didn't bother to update the metadata accordingly. So no worries about that, I'll update it and delete the package if the changes get merged here.

7 changes: 5 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ import {
createMarkerDataProvider
} from './languageFeatures.js'
import { type TailwindcssWorker } from './tailwindcss.worker.js'
import { PluginAPI } from 'tailwindcss/types/config.js'

export const defaultLanguageSelector = ['css', 'javascript', 'html', 'mdx', 'typescript'] as const

export { tailwindcssData } from './cssData.js'

export const configureMonacoTailwindcss: typeof import('monaco-tailwindcss').configureMonacoTailwindcss =
(monaco, { languageSelector = defaultLanguageSelector, tailwindConfig } = {}) => {
(monaco, options) => {
const { languageSelector = defaultLanguageSelector, ...workerData } = options || {}

const workerManager = createWorkerManager<TailwindcssWorker, MonacoTailwindcssOptions>(monaco, {
label: 'tailwindcss',
moduleId: 'monaco-tailwindcss/tailwindcss.worker',
createData: { tailwindConfig }
createData: workerData
})

const disposables = [
Expand Down
Loading