Skip to content

feat: support editor-provided styled template tags - #43

Open
usercao wants to merge 4 commits into
mainfrom
update
Open

usercao wants to merge 4 commits into
mainfrom
update

Conversation

@usercao

@usercao usercao commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces version 1 of an editor configuration protocol for appending tagged-template names at runtime. The protocol is intended for third-party editor extensions, language-server adapters, and other tsserver hosts across platforms, without tying the contract to VS Code or vscode-styled-components.

An integration that ships or loads this plugin can use the protocol to expose its own setting for compatible CSS tagged-template APIs and pass those tag names to tsserver. This can spare users from installing the package in each project, adding a plugin entry to every tsconfig.json, or copying the full base tags list for integration-specific tags. Project configuration remains available for project-specific choices, while integration defaults can stay with the integration.

The implementation:

  • keeps project tags, validate, lint, and emmet settings as the base, then appends normalized editor tags;
  • supports configuration sent before or after project creation and shares the latest valid dynamic state within one tsserver ProjectService;
  • preserves the full-replacement behavior of legacy configurePlugin payloads;
  • exports the protocol types and EDITOR_CONFIGURATION_PROTOCOL_VERSION from @styled/typescript-styled-plugin/api, and documents the host contract.

Motivation

Without an additive protocol, a third-party integration has two available paths: ask users to install and configure the plugin in each project, or send a normal configurePlugin payload. The first places integration setup in each user's tsconfig.json. The second is not a good fit for an additive setting because tsserver treats the payload as the complete plugin configuration. For example, { tags: ["globalStyle"] } replaces project-defined tags and resets validate, lint, and emmet settings.

The new envelope provides a separate additive path that preserves the project's choices:

{
  "editorConfiguration": {
    "protocolVersion": 1,
    "source": "example-editor.css-templates",
    "extraTags": ["globalStyle", "styledNative"]
  }
}

source identifies the integration, but does not act as an allowlist or a separate overlay key. Each valid payload replaces the previous overlay, including when the source changes, and an empty extraTags array clears it.

Configuration behavior

  • Base tags remain first and retain matching priority. Editor tags are trimmed and deduplicated in insertion order with case-sensitive comparison.
  • A payload with an editorConfiguration marker is treated only as an editor envelope. If it is invalid, the current dynamic state remains unchanged.
  • Legacy replacement and editor overlay are mutually exclusive. The most recent valid kind takes effect, and a later editor envelope rebuilds from the project base.
  • Payloads received before project creation are handled through PluginCreateInfo.config; the project base is recovered from compilerOptions.plugins before applying an overlay.

Limits and resilience

To keep per-request tag matching bounded, the protocol accepts a non-empty source of at most 128 UTF-16 code units, up to 64 tags, 256 code units per raw tag, and 4,096 across all raw tags. An over-limit update is left unapplied as a whole. Parsing and merging happen only when configuration changes, and repeated normalized payloads do not clear caches.

Editor envelopes and legacy runtime payloads are normalized before shared state changes. Malformed values are contained at the tsserver boundary, logs omit source and tag text, and dynamic state is scoped to one ProjectService. The scaling suite and benchmark cover the maximum legal list with a match in the last position.

If the editor marker cannot be read safely, the payload follows legacy normalization rather than being classified as an invalid editor envelope. If a host throws while the plugin reads project compiler options, the plugin records the problem and falls back to the default project base so activation can continue.

Compatibility

For users whose host does not send the new envelope, default, project, and legacy runtime configuration retain their existing behavior. The package root factory remains compatible with 1.0.1, and the public API adds only the protocol version constant and two protocol types. Real tsserver tests run against TypeScript 5.0, 5.9, and 6.0; TypeScript 4.9 retains the existing unsupported-host behavior. TypeScript 7 remains outside this tsserver-plugin protocol.

Testing

Local verification includes lint, type checking, unit and scaling tests, benchmarks, the TypeScript 5.0/5.9/6.0 tsserver matrix, and package runtime, declaration, and contents checks.

The default release-comparison suite reports 70 of 70 cases unchanged from 1.1.1.

The Node 14.21.3 and unsupported-TypeScript smoke tests remain CI-only. It would be helpful to have the release workflow wait for those jobs and the full TypeScript matrix before protocol v1 is published.

Release order

This is a backward-compatible minor capability. To make adoption predictable, host integrations can enable settings based on protocol v1 after a plugin release includes it, with the final integration artifact checked to confirm that it contains the updated plugin.

Copilot AI balanced review requested due to automatic review settings October 24, 2026 08:11
@changeset-bot

changeset-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cfc7723

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@styled/typescript-styled-plugin Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@socket-security

socket-security Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedvscode-languageserver-types@​3.18.4 ⏵ 3.18.510010080 +195 +370
Updatedvscode-languageserver-textdocument@​1.0.15 ⏵ 1.0.1610010080 +19270
Updatedvite@​8.3.4 ⏵ 8.3.599 +110082 +198100
Updatedoxfmt@​0.73.0 ⏵ 0.74.087 -71008896100
Updatedtsdown@​0.23.1 ⏵ 0.23.2981008897 +1100
Updatedoxlint@​1.88.0 ⏵ 1.89.099 +110091 +196100

View full report

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No new issues found.

This adds the version 1 editor configuration protocol cleanly: strict envelope/version/source checks, normalization (dedupe/trim, non-string entries skipped with logs), resource limits enforced with reportResourceLimits, and editor extraTags are appended on top of project config. Legacy configurePlugin payloads keep full-replacement behavior. EditorConfigurationManager and ProjectConfigurationManager wiring is exception-safe (logging best-effort, no exceptions leak into tsserver), handles before/after project creation, and shares state correctly across projects in one host. Exports/types/docs/changeset/tests all look consistent. Ready to merge as-is.

Pullfrog  | View workflow run | Using big-pickle (free) | 𝕏

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Clean-checkout typechecking is blocked, revoked configuration Proxies can abort activation, and shared-listener lifecycle memory coverage is missing.

3 open issues
What changed in this PR

Adds protocol v1 so editor integrations can append styled-template tags without replacing project settings, while preserving legacy runtime configuration behavior.

Changes:

  • Introduces bounded editor-tag overlays shared within a tsserver ProjectService.
  • Exports protocol types and a version constant, with integration documentation.
  • Adds configuration, lifecycle, package API, and performance coverage; updates dependencies.
File Description
yarn.lock Records dependency updates.
test/​unit/​tsserver-plugin.test.ts Tests configuration arrival order, sharing, and isolation.
test/​unit/​plugin-configuration.test.ts Tests normalization, overlays, limits, and hostile inputs.
test/​performance/​template-language-service.bench.ts Benchmarks matching the last allowed editor tag.
test/​performance/​scaling-check.ts Adds maximum-tag matching checks.
test/​package-api/​tsconfig.json Includes the CommonJS API consumer in typechecking.
test/​package-api/​runtime.ts Checks the exported protocol version.
test/​package-api/​runtime-esm-consumer.mjs Checks the protocol version through ESM imports.
test/​package-api/​consumer.ts Exercises public protocol types.
test/​package-api/​consumer-api.cts Exercises the CommonJS protocol API.
test/​e2e/​scenarios/​plugin-lifecycle.test.ts Tests editor overlays through real tsserver requests.
test/​e2e/​fixture-paths.ts Registers the editor-configuration fixture.
test/​e2e/​editor-configuration-project-fixture/​tsconfig.json Defines project settings for overlay tests.
test/​e2e/​editor-configuration-project-fixture/​main.ts Provides the fixture’s source file.
src/​tsserver/​tsserver-plugin.ts Restores project bases and coordinates dynamic configuration.
src/​tsserver/​tsserver-logger.ts Contains host logging failures.
src/​tsserver/​plugin-identity.ts Adds the package-name constant.
src/​tsserver/​editor-configuration.ts Implements protocol parsing, shared state, and tag merging.
src/​index.ts Scopes shared configuration to each ProjectService.
src/​configuration/​plugin-configuration.ts Defines protocol types and hardens configuration copying.
src/​api.ts Exports protocol types and version.
package.json Updates runtime and development dependencies.
docs/​usage.md Explains editor overlays and replacement behavior.
docs/​tsserver-host.md Documents project-base recovery during activation.
docs/​maintenance.md Adds protocol testing guidance.
docs/​architecture.md Specifies configuration state and lifecycle invariants.
.changeset/​quiet-tags-overlay.md Declares the additive capability as a minor release.

Review effort: Balanced

Comment thread test/package-api/tsconfig.json Outdated
Comment thread src/tsserver/editor-configuration.ts
Comment thread src/tsserver/tsserver-plugin.ts Outdated
@usercao

usercao commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

@pullfrog review

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

  • Editor configuration protocol v1 — Adds editorConfiguration envelope support to configurePlugin, with normalized parsing, resource limits, and mutual exclusivity with legacy replacement; merges extraTags into project tags case-sensitively with deduplication.
  • Configuration state management — Introduces EditorConfigurationManager and extends ProjectConfigurationManager to track dynamic configuration state (editor vs legacy), applying overlays to the project base and handling last-kind-wins across sources.
  • tsserver integration — Updates StyledPlugin to coordinate editor/project configuration lifecycle, recover project base from compilerOptions.plugins (with resilience if reading fails), and handle pre-project payloads correctly.
  • Public API and docs — Exports EDITOR_CONFIGURATION_PROTOCOL_VERSION and editor configuration types from ./api, documents the protocol in usage/architecture/tsserver-host/maintenance docs.
  • Tests and fixtures — Adds e2e scenarios for appending/clearing editor tags, pre-project payloads, kind switching, and unit/probe coverage for lifecycle behavior.

Pullfrog  | View workflow run | Using big-pickle (free) | 𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants