Move CSS linting from server to client-side - #367
Open
bartveneman wants to merge 2 commits into
Open
Conversation
Drives stylelint's linting core directly (lintPostcssResult, normalizeAllRuleSettings, createPartialStylelintResult, prepareReturnValue) instead of routing through standalone()/lint(), which statically pulls in globby, cosmiconfig and other Node-only deps. The four internals aren't part of stylelint's public exports map, so they're aliased to the real files on disk in vite.config.js and pinned to stylelint ~17.13.0 via stylelint-internals.d.ts. Replaces the /api/lint-css POST round-trip: the Linter component now fetches CSS (for URL input) via the existing client-side get_css() helper and lints it in-browser with src/lib/stylelint-browser/lint.ts, cutting out a full server round-trip per lint. CSS-scraping stays server-side in /api/get-css (Node-only deps, CORS). Includes browser-safe shims for node:os, node:process and stylelint's own bare-specifier entry point (needed because @projectwallace/stylelint-plugin's rule files import `stylelint` just to reach createPlugin/utils), plus a stub for stylelint's CLI timing/table output which isn't reachable from the browser and doesn't bundle cleanly.
✅ Deploy Preview for projectwallace ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Member
Author
|
The Audit check is failing on the pre-existing high-severity Generated by Claude Code |
5 tasks
…able Added a dedicated pinned-transitive-deps.ts, imported for its side effect from lint.ts, that imports css-tree, @csstools/css-syntax-patches-for-csstree, @csstools/css-tokenizer and fastest-levenshtein - packages stylelint's own internals (getLexer.mjs, assignDisabledRanges.mjs, reportUnknownRuleNames.mjs) import by name but our own code never did directly. Without a real usage site, dependency-usage tooling has no way to tell they're needed rather than stray entries, and pnpm's strict node_modules requires them declared in our own package.json regardless (they're normally hoisted transitively, but only for packages we actually import from). Also drops `table` from dependencies: it was added while investigating why stylelint/lib/timing.mjs doesn't bundle for the browser, but the fix ended up aliasing that whole relative import away (see vite.config.js), so the real `table` package was never reachable to begin with.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR moves stylelint-based CSS linting from a server-side API endpoint to client-side execution in the browser, eliminating the need for a backend linting service while reducing bundle dependencies.
Key Changes
New browser-based linting module (
src/lib/stylelint-browser/lint.ts): Implements a direct stylelint core runner that bypasses thestandalone()entry point and its file-resolution machinery (globby, cosmiconfig, write-file-atomic). This deep-imports stylelint internals (lintPostcssResult,normalizeAllRuleSettings,createPartialStylelintResult,prepareReturnValue) and pins their shapes via TypeScript declarations.Removed server API endpoint (
src/routes/api/lint-css/+server.ts): The POST endpoint that previously handled linting requests is no longer needed.Updated Linter component (
src/lib/components/Linter.svelte): Refactored to call the new client-sidelint()function directly instead of making HTTP requests. CSS fetching (viaget_css) remains client-side, and formatting is applied before linting.Vite configuration updates (
vite.config.js): Added resolve aliases to:node:os,node:process) and stylelint's timing modulestylelintspecifier to a shim that exposes only the public plugin APIBrowser shims (new files in
src/lib/stylelint-browser/shims/):node-os.ts: MinimalEOLexportnode-process.ts: Stubs forenv,noDeprecation,emitWarning,on,hrtimestylelint-timing.ts: No-op timing module to avoid pulling in thetablepackagestylelint-shim.ts: ExposescreatePluginand public utils without loading Node-only dependenciesPreset configuration (
src/lib/stylelint-browser/presets.ts): Extracted preset-to-rules mapping into a shared module used by both the component and tests.Test updates (
src/routes/(public)/lint-css/spec.ts): Updated to mock the CSS-fetching API instead of the linting API, and adjusted assertions to match real stylelint output.Unit tests (
src/lib/stylelint-browser/lint.test.ts): Added smoke tests verifying the deep-imported stylelint internals work correctly with the plugin.Notable Implementation Details
stylelint-internals.d.ts) manually pin the shapes of unpublished stylelint internals to stylelint ~17.13.0, with instructions to re-verify on version bumps.resolve.aliasis used (not a custom plugin resolver) because the dependency optimizer pre-bundles packages before the plugin pipeline runs.extendsresolution, custom syntaxes, caching, ignore files, or autofix support—the implementation focuses on the core linting use case.https://claude.ai/code/session_015YSKEQXzpmfmag1HcDNUaQ