diff --git a/index.d.ts b/index.d.ts index 8ff968ba..b389acd8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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 +export type TailwindConfig = Partial + +/** + * An object of the arguments for the Tailwind's built-in Plugin API. + */ +export type TailwindPluginAPI = Partial<{ + [T in keyof PluginAPI]: Parameters +}> + +type DiagnosticSeveritySetting = 'ignore' | 'warning' | 'error' export interface MonacoTailwindcssOptions { /** @@ -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<{ + editor: { + tabSize: number + } + tailwindCSS: Partial<{ + emmetCompletions: boolean + includeLanguages: Record + classAttributes: string[] + suggestions: boolean + hovers: boolean + codeActions: boolean + validate: boolean + showPixelEquivalents: boolean + rootFontSize: number + colorDecorators: boolean + files: { exclude: string[] } + experimental: { + classRegex: string[] + configFile: string | Record + } + lint: { + cssConflict: DiagnosticSeveritySetting + invalidApply: DiagnosticSeveritySetting + invalidScreen: DiagnosticSeveritySetting + invalidVariant: DiagnosticSeveritySetting + invalidConfigPath: DiagnosticSeveritySetting + invalidTailwindDirective: DiagnosticSeveritySetting + recommendedVariantOrder: DiagnosticSeveritySetting + } + }> + }> + /** + * 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. + +// 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 } /** diff --git a/package-lock.json b/package-lock.json index 1569c17c..1851769e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "monaco-tailwindcss", - "version": "0.6.1", + "name": "monaco-tailwind", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "monaco-tailwindcss", - "version": "0.6.1", + "name": "monaco-tailwind", + "version": "0.0.1", "license": "MIT", "workspaces": [ "examples/*" @@ -25,6 +25,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", @@ -9139,6 +9140,50 @@ "monaco-editor": ">=0.30.0" } }, + "node_modules/monaco-tailwind": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/monaco-tailwind/-/monaco-tailwind-0.0.1.tgz", + "integrity": "sha512-Ejb8nlCATS3tEw2QDQFoNDO4uduc5+c2liaO/aXU2qW8w1BAQkxmPGD/54Gs/XanRBC23/uXFnZmAFl2vAlR0Q==", + "license": "MIT", + "workspaces": [ + "examples/*" + ], + "dependencies": { + "@alloc/quick-lru": "^5.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", + "didyoumean": "^1.0.0", + "dlv": "^1.0.0", + "line-column": "^1.0.0", + "monaco-languageserver-types": "^0.4.0", + "monaco-marker-data-provider": "^1.0.0", + "monaco-types": "^0.1.0", + "monaco-worker-manager": "^2.0.0", + "moo": "^0.5.0", + "postcss": "^8.0.0", + "postcss-js": "^4.0.0", + "postcss-nested": "^6.0.0", + "postcss-selector-parser": "^6.0.0", + "semver": "^7.0.0", + "sift-string": "^0.0.2", + "stringify-object": "^5.0.0", + "tailwindcss": "^3.0.0", + "tmp-cache": "^1.0.0", + "vscode-languageserver-textdocument": "^1.0.0", + "vscode-languageserver-types": "^3.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/remcohaszing" + }, + "peerDependencies": { + "monaco-editor": ">=0.36" + } + }, "node_modules/monaco-tailwindcss": { "resolved": "", "link": true diff --git a/package.json b/package.json index ec47fe25..994596f7 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", @@ -32,15 +35,19 @@ ], "author": "Remco Haszing ", "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", @@ -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", @@ -82,5 +90,10 @@ "@tailwindcss/language-service": { "postcss": "^8.0.0" } - } + }, + "main": "index.js", + "directories": { + "example": "examples" + }, + "types": "./index.d.ts" } diff --git a/src/index.ts b/src/index.ts index 9c058193..cb706cdc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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(monaco, { label: 'tailwindcss', moduleId: 'monaco-tailwindcss/tailwindcss.worker', - createData: { tailwindConfig } + createData: workerData }) const disposables = [ diff --git a/src/tailwindcss.worker.ts b/src/tailwindcss.worker.ts index e0432d0c..74589a28 100644 --- a/src/tailwindcss.worker.ts +++ b/src/tailwindcss.worker.ts @@ -4,12 +4,19 @@ import { doComplete, doHover, doValidate, + EditorSettings, type EditorState, getColor, getDocumentColors, - resolveCompletionItem + resolveCompletionItem, + Settings, + TailwindCssSettings } from '@tailwindcss/language-service' -import { type MonacoTailwindcssOptions, type TailwindConfig } from 'monaco-tailwindcss' +import { + TailwindPluginAPI, + type MonacoTailwindcssOptions, + type TailwindConfig +} from 'monaco-tailwindcss' import { type TailwindWorkerOptions } from 'monaco-tailwindcss/tailwindcss.worker' import { initialize as initializeWorker } from 'monaco-worker-manager/worker' import postcss from 'postcss' @@ -34,6 +41,7 @@ import { import { TextDocument } from 'vscode-languageserver-textdocument' import { type JitState } from './types.js' +import { PluginAPI } from 'tailwindcss/types/config.js' export interface TailwindcssWorker { doCodeActions: ( @@ -62,7 +70,8 @@ export interface TailwindcssWorker { } async function stateFromConfig( - configPromise: PromiseLike | TailwindConfig + configPromise: PromiseLike | TailwindConfig, + intellisense?: { editor?: EditorSettings; tailwindCSS?: Partial } ): Promise { const preparedTailwindConfig = await configPromise const config = resolveConfig(preparedTailwindConfig) @@ -103,13 +112,16 @@ async function stateFromConfig( }, // eslint-disable-next-line require-await async getConfiguration() { + const { editor, tailwindCSS } = intellisense || {} + const { classAttributes = [], lint = {}, ...tailwindOptions } = tailwindCSS || {} + return { - editor: { tabSize: 2 }, + editor: { tabSize: 2, ...editor }, // Default values are based on // https://github.com/tailwindlabs/tailwindcss-intellisense/blob/v0.9.1/packages/tailwindcss-language-server/src/server.ts#L259-L287 tailwindCSS: { emmetCompletions: false, - classAttributes: ['class', 'className', 'ngClass'], + classAttributes: ['class', 'className', 'ngClass', ...classAttributes], codeActions: true, hovers: true, suggestions: true, @@ -123,7 +135,8 @@ async function stateFromConfig( invalidVariant: 'error', invalidConfigPath: 'error', invalidTailwindDirective: 'error', - recommendedVariantOrder: 'warning' + recommendedVariantOrder: 'warning', + ...lint }, showPixelEquivalents: true, includeLanguages: {}, @@ -135,7 +148,8 @@ async function stateFromConfig( classRegex: [], // Upstream types are wrong configFile: {} - } + }, + ...tailwindOptions } } } @@ -165,7 +179,27 @@ export function initialize(tailwindWorkerOptions?: TailwindWorkerOptions): void ) } - const statePromise = stateFromConfig(preparedTailwindConfig) + const formattedConfig = { + ...preparedTailwindConfig, + plugins: [ + ...(preparedTailwindConfig.plugins || []), + (pluginAPI: PluginAPI) => { + const plugins = options.pluginAPI + const apis = Object.keys(plugins || []) as (keyof PluginAPI)[] + + apis.forEach((api) => { + if (!plugins || !(api in pluginAPI)) return + + const builtInFunction = pluginAPI[api] + const args = plugins[api] + //@ts-expect-error + builtInFunction(...args) + }) + } + ] + } + + const statePromise = stateFromConfig(formattedConfig, options.intellisense) const withDocument = ( diff --git a/tailwindcss.worker.d.ts b/tailwindcss.worker.d.ts index c9531b8a..79295f7c 100644 --- a/tailwindcss.worker.d.ts +++ b/tailwindcss.worker.d.ts @@ -10,7 +10,7 @@ export interface TailwindWorkerOptions { * @returns * A valid Tailwind configuration. */ - prepareTailwindConfig?: (tailwindConfig?: TailwindConfig | string) => Config | PromiseLike + prepareTailwindConfig?: (tailwindConfig?: TailwindConfig | string) => Config } /**