Giter Site home page Giter Site logo

proposal-fel's Introduction

Front-End Language Reliability Tool

Disclaimer: This intent of this document is to discuss ways to improve upon existing build tools in a pragmatic but positive manner. If anything is unclear or if you have any questions, please raise them as a Github issue.

What are the primary problems we want to solve?

The loose decoupled nature of HTML and CSS makes it difficult to safely remove unused rules and determine what rules may conflict with each other. Not only that, but various browsers vendors such as Internet Explorer and Safari either lag behind the latest standards by half a decade or implement a feature erroneously, which pushes additional development effort to the website or web app developer.

We don't only want this tool to solve the above problems, but solve them well. This means a few things:

  • It needs to be blazing fast. CSS preprocessors like SASS aren't very complex but seem to compile significantly slower than other build tools like the TypeScript compiler. For example, on a Windows machine, SASS might take 4 seconds to recompile in "watch mode" for a reasonably large project using a Bootstrap-sized framework.
  • It needs to be 100% backwards compatible. If it's installed globally on a developers machine, it should be easy to install the next version (eg. 1.1) but also be able to run the tool at an older version (eg. 1.0). The Go programming language claims to offer this feature using a "-lang" flag.
  • The tool should not rely on the user having Python, NodeJS or any other third-party dependency installed, it should work standalone. However, it should also be easy to integrate with existing build systems such as Webpack. We might want the Webpack version to be a JavaScript / WASM build of the tool as building native binaries like NodeSASS can cause problems with build servers and compose poorly with other tools like Cypress.
  • It should ideally always have at least 2-3 deeply invested maintainers so that the project isn't halted by a single author leaving the project.

Who do we want to solve these problems for?

We want to solve this problem in a way that will be a good fit for both large JavaScript build tool projects as well as for a time and budget-constrained work.

If you aren't aware, to get an idea of the time constraints some web developers are under, especially beginners, Im talking about projects that have about 45-90 hours of total budget. That means a CMS backend with modules or plugins needs to be setup, the frontend needs to be built and then finally all that code needs to be put on a remote server in 2 weeks.

Another consideration is quick quoted work that can take anywhere between 1 to 6 hours. If you quote a client with 6 hours of work, then you most likely aren't going to be able to prioritize HTML and CSS cleanliness if you underestimated the effort. However, having a tool that just tells you what's unused or that could just cleanup unused rules for you, would save a lot of time and improve readability.

What are some existing solutions to these problems?

Before considering building yet another tool, we should look around and see if there's anything out there that will be able to solve the problems we have.

One common thing I've listed as a "Con" below is the reliance on a JavaScript build tools. The reason for this is that in my experience when doing budget-constrained work, you don't have time to deal with the inertia and unreliability that comes from using these tools.

Determine what CSS rules are actually used by a project

Here are a list of tools or resources that help assist you in finding what CSS rules are being used.

Chrome DevTools Audit

A tool built-in to the Chrome web browser which can help you discover unused CSS styles.

Pros

  • 100% accuracy

Cons:

  • Requires loading each page and all its possible variants, ie. if a client can modify a banner to have different styling, that variant needs to be tested. This takes time!

Typed CSS modules or similar

Tools that integrate with a JavaScript Build System and generate identifiers so that when a CSS rule is removed, a variable will become undefined thats tied to that CSS rule.

Implementations:

Pros:

  • Generate constants from CSS files so that your JavaScript build will fail if you're attempting to use a rule that was removed.

Cons:

  • Assumes you're building an application or using a build tool. Not helpful for those with legacy HTML/CSS code or those building Wordpress sites.

PurifyCSS

Pros:

  • Can process HTML, JS and PHP to see what classes are used/unused.

Cons:

  • Project is no longer maintained.
  • Assumes you're using a build tool like Gulp, Grunt or Webpack. Would require the user have Node installed.
  • Requires manually specifying the files you want to scan. Ideally, we want something that will "just work".

Stop CSS rules from conflicting

Here are a list of practices or tools that were designed to stop CSS classnames from conflicting with each other and that also keep CSS specificity low, which helps reduce cognitive complexity.

CSS Modules

Pros

  • Stops CSS conflicting by namespacing
  • Keep CSS specificity low
  • Not a process. The tool automatically namespaces your rules for you, less room for error.

Cons

  • Requires a JavaScript build tool like Webpack

Pros

  • Stops CSS conflicting by namespacing
  • Keep CSS specificity low

Cons

  • Unintuitive and hard to understand. We base this on personal experience where either a client has provided frontend where BEM wasn't followed properly or where teams with not much experience in frontend get it wrong. We don't see this as a failing of developer, but rather the unintuitive aesthetic of BEM itself.

What HTML patterns or CSS features have erroneous behaviour in certain browsers

Here are a list of tools or resources that help assist you in finding what HTML is valid and what CSS rules are supported.

W3C Markup Validation Service

A website that tells you whether your HTML is correct or not.

Pros

  • Tells you whether your HTML is valid or not.
  • Historically, this was very good at helping you figure out why Internet Explorer 6, 7 or 8 wasn't working, usually due to a unclosed or incorrectly closed tag.

Cons

  • Not built-in. Requires you provide a live URL or paste HTML per-page.

CanIUse

A website that will tell you what features are either partially or fully supported in a browser.

Pros

  • Informs you of partial implementations that may differ or have buggy behaviour when compared to the W3C spec.

Cons

  • Not a built-in tool, which makes it easy to accidentally use a bleeding edge CSS feature without realizing.
    • Side Note: Manual testing in your target browsers would help avoid this but unfortunately we have rarely seen developers test in those browsers either due to lack of physical ownership (iPhone), lack of knowledge on how to test older Internet Explorer in a VM or laziness.

How do we solve this problem

Now that we've defined at least some problems, let's looks at some things that our tool will need to be able to solve.

Nested CSS

In a legacy HTML or CSS project, nested CSS is definitely going to exist in the project. By nested CSS, we mean a selector such as ".myElement .myOtherElement". Due to the nature of various legacy systems and also with how Wordpress works, this tool is going to need to be able to reason about how templates include other templates so that it is able to build an accurate model of the varying HTML structures to see if that CSS rule is used or not.

This becomes more complicated as different backend configurations could completely change what CSS classes get rendered. If a user has a website wherein a client is configuring CSS classes directly and storing them in a database, we are limited in how much an external tool can reason about what is used / unused.

ie.

<div class="<?php echo $myClasses; ?>"></div>

$myClass came from a MySQL database or something that we have no control over.

One solution to this problem is that a user could add additional code so that a tool could reason about what classes exist in scope.

<?php
$classesAvailable = [
  “button” => true,
  “button-primary” => true,
];
// Split string of classes into an array of class names.
$classes = preg_split('/\s+/', $myClasses, -1, PREG_SPLIT_NO_EMPTY);
$myClassesValidated = “”;
foreach ($classes as $class) {
  if (isset($classesAvailable[$class])) {
    $myClassesValidated .= $class;
  }
}
?>
<div class=<?php echo $myClassesValidated; ?>>
</div>

By doing the more verbose approach above, we've given the compiler enough information to know that $myClassesValidated can only have up to 2 classes, "button" and "button-primary".

Another solution is that within our CSS, we simply add a note that states that we don't want the compiler to check if that rule is used or not. This would also be useful for stateful CSS classes that the tool might not be able to reason about. Ie. JavaScript applied classes

// fel:unchecked
.button {
  display: block;
}

Linting tools such as TypeScript Lint allow you to ignore rules certain properties / patterns with a code comment.

Finally, we could avoid needing to get an accurate model of the HTML by forcing developers to use something like BEM or CSS Modules, wherein they keep rules simply by only targeting 1 class without nesting.

Built-in and custom rules

There should be built-in rules to stop users from using incorrect HTML tags. For example, using an anchor link as a button should be banned by default, ie.

<a href="#" class="btn"></a>

Would recommend you switch to:

<button type="button" class="btn"></button>

or at the very least, apply the button role.

<a href="#" class="btn" role="button"></a>

There could also either be built-in or user-defined CSS rules that assist with:

  • Warning the use of viewport height units, as they don't work as expected on iOS.
  • Warning the use of position: absolute / relative without defining an explicit z-index. We've personally found that when you don't at least explicitly give a z-index of 0, you get unpredictable rendering order across browsers.

Framework / Language Agnostic

Use cases for our tool

  • A Wordpress project that is made up of PHP, JavaScript and CSS/SASS files.
  • A TypeScript React application that is made up of TypeScript files and pre-processed CSS files (most likely SASS as it seems to the most popular)
  • A SilverStripe project is going to be up of PHP, SS templates (it's own template format!), JavaScript and CSS.
  • A Go project made up of Go files, HTML templates (handlebar pre-processing), JavaScript and CSS.

To keep code written in this tool stable and reliable with minimal complexity, simply reading or validating multiple file formats seems out of the question. New features are being added to PHP, JavaScript and TypeScript all the time which could break the parser of our hypothetical tool. If this tool is to have strong backwards compatibility, it will most likely need its own format for templating that can at least target PHP, JavaScript and TypeScript. Ideally it would give it's users the ability to target whatever esoteric template language it needs to.

However, just because we may need our own template parser, that doesn't mean it needs to be its own special esoteric syntax or language. Infact, just using HTML with something simple like Mustache template syntax might be all we want.

proposal-fel's People

Contributors

silbinarywolf avatar

Stargazers

Phillip Trudeau avatar Petar Simic avatar zanderwar avatar

Watchers

James Cloos avatar

proposal-fel's Issues

Initial Thoughts

Preface

So I've been using Node-based JS build tools for about 3 years now. It's become a pretty graceful node-cli-type-application to orchestrate several tools rather than rely on the dependency hell that Grunt or Gulp foists upon you for the luxury of a sugary single build script.

I can definitely appreciate an effort to provide a more one-size-fits-all solution for the CSS/HTML/JS disparities. Judging from the success of Typescript (and from what you touched on in the proposal), the key is a light-touch approach where the tool can wrap trivially around existing projects and allow gradual migrations. I am aware JS is not mentioned much in the scope of this document, and I'll omit it mostly from my initial points to keep the starting scope tight.

Overall Impressions

I'll include this section as an dictum of my understanding of what is being proposed:

  • This is a globally installed tool that can be run on a web project of any size
  • It will possibly be built in Go, but will definitely not be Ruby or Python. Most likely not Haskell either.
  • It's primary initial focus is static analysis of...
    • keeping CSS correct, compatible and structured orthogonally with a provided HTML (and/or JS) project
    • Ensure HTML is valid XML structure and can contain sugar (JSX, Mustache template, etc)
    • Ensure HTML and CSS align and rules are not unused or nested incorrectly
    • Possibly the same for JS (and templates-in-JS)

Giant Mess of Thoughts

There's definitely a slow part of CSS preprocessing.

It needs to be blazing fast. CSS preprocessors like SASS aren’t very complex but seem to compile significantly slower than other build tools like the TypeScript compiler

SASS may be a weird example since it's entire compiler ecosystem consists of libSass, which is a Rubygem (IIRC) and is painfully slow. That being said, LESS and Stylus seem to suffer from similar problems, most likely also compiling from a slower interpreted language.

My tool-chains for CSS however always seem to consist of:

optionally strip CSS with webpack loader -> SCSS/LESS/Stylus compiler pass -> clean-css pass -> postCSS pass -> write file to disk

Essentially this is analogous to:

text extraction (optional) -> linking -> static analysis -> compiling -> minification -> compatibility sugar -> filesystem IO

linking, static analysis, compiling are through the single libSass pass and are by far the slowest part of the build section. From memory it measures around 55% of build time, with filesystem IO taking another rough 12% which I generally account to be done using Node. Minification of course adds a fair chunk (~32%) which is to be expected, but is quite fast given the size of the CSS being processed (~3mb @ ~900ms).

Ok? What's this got to do with the tool?

Stop CSS rules from conflicting

This is a much larger problem that is endemic of CSS' core design rather than through failings of current solutions to correct it. In the current ecosystem, I've found a lot of people tend to extract CSS from webpack loader output, which often scope the CSS themselves (such as vue-loader's scoped attribute).

I have a couple of ideas about what points would be beneficial to focus on, but my stance is I think it's a problem better reasoned about later in the tool's development:

  • Ensure naming of classes are consistent with "how" they are used...
    • such as matching their parent module base class (a BEM-like-principle)
    • modifier classes have prefixes and are generally considered to be added dynamically (and so should not flag if not used in the HTML document)
  • Ensure JS-generated HTML is analysed in the same manner, including where .className/.classList is used
  • Ensure JS selected elements are "in-DOM" at the time of selection.

What CSS properties or features have erroneous behaviour in certain browsers

Tools that correct CSS compatibility issues (usually by implementing CanIUse directly through their DB) are more extensive than pre-processors and already quite fast, and also offer some linting support in the case of PostCSS, but it's plugin ecosystem is a bit NPM messy.

Since this tool is intended to gracefully wrap around a project which may or may not have a large existing pre-processor-styled code-base, I believe there's a couple of specific problems we can solve for CSS to add benefit to the ecosystem:

  • Fast static analysis of CSS validity; specifically rules and formatting that is orthogonal across pure CSS and pre-processor languages
  • Can (optionally) link many files based on @import (or like) statements. This would potentially allow further tooling to be run on a single output (much like what happens following a node-sass pass)
  • Can minify CSS code blazingly fast.

This means that FEL would be essentially (from a pure-CSS standpoint) able to replace about half of libSass' processing needs, specifically:

  • Linting rules can be disabled in node-sass itself
  • libSass (or LESS/Stylus equivalent) would no longer need to link multiple imported files together, which in each pre-processor has some terrible error handling quirks.

Nested CSS

At a high level, I believe what you are talking about is the idea of comparing a sort of abstract syntax tree of CSS specificity and comparing it to an HTML document's structure, flagging any CSS definitions that do not match HTML structure.

If this is the case, a possible solution (or one of) would be to allow developers to define a global CSS source for known global classing that the compiler can assume may slot into any dynamic class rendering. This would also align with the common application design philosophy of a "single source of truth design system", and allow tools down the line to consume these global classes or (possibly) variables.

Framework / Language Agnostic

This brings up the issue that it may be necessary for a plugin system. Fel would say initially support only Moustache Templates and JSX (the post popular I can think of), but allow an API for developers to write their own plugins. This is a Hard Problem, and I don't really have many ideas about how to solve it (I am not as well versed in compilers as I make it sound, and I don't make it sound like I know much).

End Bit

I've rambled a lot here and have created a mess, but it'll be important at these early stages for all people involved in planning - and possibly development - take some time to get onto the same page. Feel free to pick apart my ideas as to whether they suit your vision or not and I'll slowly get a better idea in my head.

Replace all camelCased variables with underscores.

Reasons to do this would be:

I'd like additional research on this, but my personal feeling is that underscores are much easier to read. That being said, because Golang convention is camelCase, the compiler itself will still use this format.

Mirror:
https://web.archive.org/web/20160416063844/https://whathecode.wordpress.com/2013/02/16/camelcase-vs-underscores-revisited/

Papers referenced in blog post above: (broken as of 2017, but can be found in Google Scholar):

  • Woman and men – Different but equal: On the impact of identifier style on source code reading by Sharafi et al. (2012) (PDF)
  • Context and Vision: Studying Two Factors Impacting Program Comprehension by Soh, Z. (2011)
  • Can Better Identifier Splitting Techniques Help Feature Location? by Guerrouj et al. (2011) (PDF)

Add `events` key to component definition.

JavaScript has a rather nice events system, and helps with communication between parent and child components.

It would be nice to be able to specify events in a component like so:

events [
    'select', //Use a string for a default (bubbles/cancellable) event
    'set_time',
    {
        name := 'set_date',
        bubbles := false
    }, //Or an object to detail the event
    ....
]

Then wherever behaviour is defined*, you can emit an event like so; element.emit('select', extra)
or listen: element.listen('select', fn).

Each event name when actually emitted at runtime should be prepended by the component name and a dot ., Eg: 'Button.select'
If a namespace is set in the build config, maybe that can also be prepended in the same way?

*Which leads into another question about where behaviour is defined lel. I'll make another issue for this.

Optional `for` item scope name

This will probably be in line with Silverstripe/Twig loops, which implicitly uses the scope of the current item, but not sure. Might have something like: "for MyList {" wherein you access properties with "it.Name" Reasoning for this is so that when reading the code, you immediately know if the code your reading is in a "for" loop context. Also means you dont need special "top" or "up" variables to access parent scopes.

I like this idea, but sure it wouldn't hurt to add an optional name for your scope item. Wherin:

//Implicit
for MyList {
    // MyList props accessed like: it.Name
}

//Named
for item in MyList {
    // item.Name
}

Just think the option means more idiomatic code for developers to get on board (part of why I've seen people even shy away from python lel).

Using template literals

Under Reusable Components

button(class="js-component-button "+kind+" "+width, type=type)

Apart from the fact we're missing a " here. Should read:

button(class="js-component-button "+kind+" "+width, type=type)

(((N I T P I C K I N G)))

It would be nice to take a page out of JavaScript's book here, mainly because I have a hate for concatenation. Just an awkward syntax to write when dealing with lots of strings (especially in class names). Eg:

button(class="js-component-button ${kind}${width}", type=type)

Not necessarily the ${} syntax, but you get the idea. Nice-to-have idea I guess.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.