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

Add support for running in a browser #3935

Open
oskarkrawczyk opened this issue Feb 1, 2019 · 24 comments
Open

Add support for running in a browser #3935

oskarkrawczyk opened this issue Feb 1, 2019 · 24 comments
Labels
status: ask to implement ask before implementing as may no longer be relevant type: enhancement a new feature that isn't related to rules

Comments

@oskarkrawczyk
Copy link

I'm currently trying to replace CSSLint in @jsfiddle with Stylelint. Got it working thanks to a proof-of-concept made here https://github.com/m-allanson/stylelint-browser-demo

Unfortunately turns out this works fine but with a very old version of Stylelint, specifically the 5.x branch. Once updated deps to the newest Stylelint, the whole client-side bundle breaks apart with a slightly cryptic Error: Cannot find module "." error.

I was wondering if anyone around here can help out figure out if it's even possible to run the newest Stylelint on the client-side (this is a requirement for JSFiddle as our env only supports linting that way).


A bit more on what my config looks like:

webpack.config:

import webpack from "webpack"
import path from "path"

export default {
  entry: "./site/index.js",
  output: {
    path: path.resolve(__dirname, "site"),
    publicPath: "/",
    filename: "bundle.js",
  },
  resolve: {
    root: path.resolve(__dirname),
    alias: {
      "resolve-from": "empty-module",
      cosmiconfig: "empty-module",
      doiuse: "empty-module",
      globby: "empty-module",
      globjoin: "empty-module",
      multimatch: "empty-module",
      path: "empty-module",
    },
    modulesDirectories: [
      "node_modules",
      "site",
    ]
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.json$/,
        loader: "json-loader",
      },
    ],
  },
  plugins: [
    new webpack.optimize.DedupePlugin(),
    new webpack.ContextReplacementPlugin(/stylelint/, /NEVER_MATCH^/),
  ],
  node: {
    fs: "empty",
    module: "empty",
  },
}

package.json:

{
  "name": "stylelint-browser-demo",
  "version": "0.0.1",
  "description": "A proof-of-concept to demo stylelint running in a browser.",
  "main": "site/bundle.js",
  "scripts": {
    "start": "npm run build && http-server ./site",
    "start-dev": "webpack-dev-server --content-base site/ --hot --inline",
    "build": "webpack --progress --colors && stat -f%z site/bundle.js",
    "lint": "eslint . --ignore-path .gitignore",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "m-allanson",
  "license": "MIT",
  "dependencies": {
    "babel-cli": "^6.1.18",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.6.0",
    "http-server": "^0.9.0",
    "imports-loader": "^0.6.5",
    "json-loader": "^0.5.4",
    "stylelint": "^9.10.1",
    "stylelint-config-standard": "^18.2.0",
    "webpack": "^1.12.14"
  },
  "devDependencies": {
    "eslint": "^2.4.0",
    "eslint-config-stylelint": "^1.0.0",
    "webpack-dev-server": "^1.14.1"
  },
  "engines": {
    "node": "5.9.1",
    "npm": "3.7.3"
  },
  "eslintConfig": {
    "extends": "stylelint",
    "env": {
      "browser": true
    },
    "rules": {
      "arrow-spacing": 2,
      "no-var": 2,
      "object-shorthand": 2,
      "prefer-const": 2,
      "template-curly-spacing": 2
    }
  },
  "babel": {
    "presets": [
      "babel-preset-es2015"
    ]
  }
}

The build bundle.js is attached - bundle.js.zip

Lint method is exposed as Stylelint.verify()

@ai Any chance you could help out? (we had a brief chat about this on Twitter).

@ai
Copy link
Member

ai commented Feb 1, 2019

Seems like we have too dymanic require somewhere.

I think client side use case is important. PostCSS and Autoprefixer both support it.

@jeddy3
Copy link
Member

jeddy3 commented Feb 1, 2019

Unfortunately turns out this works fine but with a very old version of Stylelint, specifically the 5.x branch

We ran into our own difficulties trying to get stylelint to run client-side. We ended up going server-side for our demo on the website. However, this was a couple of years ago and the tooling might have moved on enough to take another crack at it.

Seems like we have too dymanic require somewhere.

The heaviest parts of stylelint are the:

  • parsers
  • rules

I believe we use dynamic requires for these.

They were added to improve the bad performance outlined in #2454. However, there's probably a better way. One that improves performance and caters for the client side use case.


I've made a couple of attempts recently to use ncc but the load-time of stylelint seemed to worsen so I didn't pursue it.

A good way forward might be to adopt prettier's approach to building. They are dealing with similar problems to us:

  • the need for a client-side (standalone) build
  • bundling a number of (pretty heavy) parsers

And they have far more contributors dedicating their time to the problem.

Or it might be that unpicking some of our dynamic requires to support your webpack config will require less effort and time.

Has anyone else got any ideas?

@ai
Copy link
Member

ai commented Feb 1, 2019

I think we can have internal API for client-side and auto-load API for CLI:

// Client-side

let core = require('stylelint/…')
let scss = require('postcss-scss')

core.check(source, { parser: scss })
// CLI
let autoloader = require('stylelint/…')

autoloader.autodetect(source, filename) // will require core and scss dynamically inside

@oskarkrawczyk
Copy link
Author

Thanks for chiming in guys (it's rare to see contributors acutally responding on GHI these days).

Or it might be that unpicking some of our dynamic requires to support your webpack config will require less effort and time.

Slightly out of my understanding how all of this might be put together to work, so I think for now I'll stick to using the 5.x branch (6.x also kind of works, but there is a JS error, 8.x doesn't throw errors but for some reason the verify promise doesn't seem to fulfill [on the client side]).

I have my hopes someone has the time to find a way and make Stylelint work on the client, that would really be tremendous.

@jeddy3
Copy link
Member

jeddy3 commented Feb 3, 2019

I've looked into this a couple more times, but I wasn't able to make much headway.

Unfortunately, I don't foresee myself having time to dig deeper anytime soon.

I think we can have internal API for client-side and auto-load API for CLI:

Sounds good to me. It'll help with the weight of the parsers. I believe this is similar to what prettier does.

I have my hopes someone has the time to find a way and make Stylelint work on the client, that would really be tremendous.

I'm going to label this issue up as an enhancement and help wanted. Hopefully, this issue will pique someone's interest and they'll champion this feature.

@jeddy3 jeddy3 changed the title Building Stylelint for client-side Add support for running in a browser Feb 3, 2019
@jeddy3 jeddy3 added status: ready to implement is ready to be worked on by someone type: enhancement a new feature that isn't related to rules labels Feb 3, 2019
@oskarkrawczyk
Copy link
Author

I'm going to label this issue up as an enhancement and help wanted

Sounds good! I'll push this issue though JSFiddle channel, perhaps someone will have the resources to look into this. If not, do you mind if I post it as a bounty? I want to be 100% sure you're ok with that.

@jeddy3
Copy link
Member

jeddy3 commented Feb 3, 2019

If not, do you mind if I post it as a bounty?

Sounds good to me!

@ai
Copy link
Member

ai commented Feb 4, 2019

I will promote this issue in PostCSS twitter too.

@konpikwastaken
Copy link

Couldn't sleep, wanted to see if it could be hacked together to run similarly to the POC done earlier. Hopefully this might help someone in the future.

tl;dr; it's possible in the current state

https://github.com/konpikwastaken/stylelint-browser-poc

@alexander-akait
Copy link
Member

@konpikwastaken great job 👍

@m-allanson
Copy link
Member

It's great to see work happening around this!

@konpikwastaken's comment on their linked repo is possibly the key to making this work well:

The engine should be refactored & decoupled from anything that depends on an actual file system (e.g. reading configs, etc). Majority of the time was spent working around assumptions that the execution environment has IO access (e.g. path, fs).

@jeddy3
Copy link
Member

jeddy3 commented Feb 18, 2019

The engine should be refactored & decoupled from anything that depends on an actual file system (e.g. reading configs, etc).

Sounds great!

I think we can close this issue when someone finds time to do that refactoring.

@jeddy3 jeddy3 added type: refactor an improvement to the code structure and removed type: enhancement a new feature that isn't related to rules labels Feb 18, 2019
@jeddy3
Copy link
Member

jeddy3 commented Apr 30, 2020

The engine should be refactored & decoupled from anything that depends on an actual file system (e.g. reading configs, etc). Majority of the time was spent working around assumptions that the execution environment has IO access (e.g. path, fs).

@m-allanson and I are going to look into this.

It'd be great if we ended up with something similar to how Prettier does it:

<script src="https://unpkg.com/stylelint@14.0.0/universal.js"></script>
<script src="https://unpkg.com/stylelint@14.0.0/syntax-scss.js"></script>
<script>
  stylelint.lint("a#{var} { color: red; }", {
    config: { "rules": { "color-named"} },
    syntax: "scss",
  });
</script>

The issue is related to #2454, where we need to resolve some performance issues.

@jeddy3 jeddy3 added type: enhancement a new feature that isn't related to rules status: wip is being worked on by someone and removed type: refactor an improvement to the code structure status: ready to implement is ready to be worked on by someone labels Apr 30, 2020
@m-allanson
Copy link
Member

@m-allanson and I are going to look into this.

In fact we have already spent some time working on this 😄. You can see a live demo of progress here: https://upbeat-yonath-79931e.netlify.app

I think some refactoring of stylelint's internals would make this quite achievable, and would allow us to avoid previous approaches of stubbing out modules that access the filesystem.

Check out this repo to see how the demo works. It's a relatively small amount of code.

@m-allanson
Copy link
Member

Following @jeddy3's changes in #4729 I've got a WIP branch here: https://github.com/stylelint/stylelint/compare/browser-bundle. It's still pretty rough but demonstrates that this should work.

Syntax parsers are bundled separately and can be loaded on demand.

The stylelint bundle comes out at 250KB (minified + gzipped), with the syntax parsers ranging between 30KB and 260KB.

bundle min + gzip min only
stylelint 251K 1.3MB
CSS in JS 260K 1.0MB
HTML 120K 433K
Less 31K 106K
Markdown 145K 509K
Sass 71K 287K
Scss 32K 109K
SugarSS 33K 113K

There's an updated demo at https://stylelint-browser-bundle.netlify.app/. Check the network tab when selecting a syntax to see parsers being loaded in as needed.

@jeddy3 jeddy3 mentioned this issue May 6, 2020
6 tasks
@m-allanson
Copy link
Member

I've opened a draft PR that details the current progress of this feature: #4796

@aprTaylor
Copy link

Any updates on this?

@adalinesimonian
Copy link
Member

VS Code now runs in the browser as well as on desktop. In theory, if browser support ever does get implemented, it may then be possible to have the VS Code extension run in the browser as well since vscode-languageserver has browser support.

@jeddy3
Copy link
Member

jeddy3 commented Nov 8, 2021

I saw that. Very exciting stuff.

I don't think we've far off. @m-allanson has mostly got us there. I think we'll need to:

  • Move to ESM #5291
  • Remove processors (which can only happen when there's a robust and maintained styled-components custom syntax)

@adalinesimonian
Copy link
Member

I don't think we've far off. @m-allanson has mostly got us there. I think we'll need to:

* [Move to ESM #5291](https://github.com/stylelint/stylelint/issues/5291)

I'll need to investigate a few things downstream. I remember I was having issues using ESM in VS Code while testing. However, I don't really remember exactly what went wrong. I'll take a look into it, maybe after I take care of a few issues I'm working on in v1.2.

@ota-meshi
Copy link
Member

I'm experimenting with getting stylelint to work on browser.
Demo: https://ota-meshi.github.io/stylelint4b/Playground.html

I now realize that there are a lot of tricks required to get stylelint working on the browser.
I would like to share those information.

  • Stub

We need to replace some file-related external modules with modules that work on the browser.
https://github.com/ota-meshi/stylelint4b/blob/e210f823992126c610c144fb3e3da817167b2c73/packages/stylelint4b/build/index.js#L51

Also, stylelint's internal module needs to be replaced with modules that works on the browser.

https://github.com/ota-meshi/stylelint4b/blob/e210f823992126c610c144fb3e3da817167b2c73/packages/stylelint4b/build/index.js#L17

  • Replace source code

Some source code needs to be replaced and rewritten for the bundle.

https://github.com/ota-meshi/stylelint4b/blob/e210f823992126c610c144fb3e3da817167b2c73/packages/stylelint4b/build/index.js#L35

  • Configuration and plugin resolution

It hacks the require function to resolve plugins and shareable configs.

https://github.com/ota-meshi/stylelint4b/blob/e210f823992126c610c144fb3e3da817167b2c73/packages/stylelint4b/src/require-shim.js

Users can resolve plugins and shareable configurations by pre-registering aliases.

https://ota-meshi.github.io/stylelint4b/stylelint4b/#stylelint4b-alias
https://github.com/ota-meshi/stylelint4b/blob/e210f823992126c610c144fb3e3da817167b2c73/packages/stylelint4b/src/alias-module.js

Unless you use plugins or shareable configs, we probably don't need it.

@ybiquitous
Copy link
Member

@ota-meshi Thanks for the share. That's an interesting trick! I now understand well what we need for running Stylelint on browsers.

@abose
Copy link

abose commented Dec 31, 2022

Hi all, I was trying to integrate stylelint too for phcode.dev, the browser version of braclkerts.io code editor.

Is it still not supported/how much work is left to port it to the browser?
Many thanks for considering my request.

@eight04
Copy link

eight04 commented Jan 2, 2023

Stylus is a usercsss browser extension, which uses stylelint as the CSS linter in the editor. Therefore we have a prebuilt bundle here:
https://github.com/openstyles/stylelint-bundle

We did shim a lots of modules, see:
https://github.com/openstyles/stylelint-bundle/blob/master/rollup.config.js

@Mouvedia Mouvedia added status: ask to implement ask before implementing as may no longer be relevant and removed status: wip is being worked on by someone labels Apr 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: ask to implement ask before implementing as may no longer be relevant type: enhancement a new feature that isn't related to rules
Development

Successfully merging a pull request may close this issue.