denoland / deno

A modern runtime for JavaScript and TypeScript.
https://deno.com
MIT License
93.37k stars 5.18k forks source link

Support CSS Modules #11961

Open kitsonk opened 2 years ago

kitsonk commented 2 years ago

Chromium is shipping CSS Modules unflagged in v93: https://chromestatus.com/feature/5948572598009856

We should consider how this applies to Deno.

The relevant commit to the HTML Living Spec: https://github.com/whatwg/html/commit/3d45584d286e9455cc24ebae1f3aca3db120dc9d

The biggest challenge is that a constructed CSSStyleSheet object is returned on the import which then is inserted into the DOM. Deno doesn't have a DOM or the CSSStyleSheet object, nor is it clear what expectations would be for users and varies greatly from what a front-end bundler would consider a "CSS module".

Personal opinion is that everything is very vague that it really is unimplementable. (Plus we are also blocked on having import assertions, which are still not supported by tsc)

jtoppine commented 2 years ago

CSSStyleSheet and friends sound like out of scope for deno perhaps better suited to a third-party module, but having low level access to CSS modules in deno would be useful. I can imagine a use case to only need:

1) ability to programmatically inspect and read (just raw text data) CSS imports of any given module and it's dependencies 2) CSS and JSON imports being bundled/compiled alongside js.

When the same JS modules are used both for server side rendering as well as client side browser code, this would enable server applications to somehow collect the CSS modules used and present them to the client as a traditional, pre-baked CSS file when SSR is being used.

Frameworks or server apps would be free to implement this kind of functionality however they best see fit, but I can imagine it being desireable for many to have CSS dependencies managed alongside JS.

Macil commented 1 year ago

For anyone else who is confused like I was: the CSS Modules feature talked about here (sometimes disambiguated as "CSS Module Scripts") is unrelated to the popular CSS Modules feature (import styles from "styles.module.css";) supported by webpack/css-loader, Vite, Parcel, Next.js, Create React App, the Deno framework Aleph.js, etc, and it's also unrelated to all of those tools' ability to import non-module CSS files to load them as regular global stylesheets (import "styles.css";).

The CSS Module Script standard isn't used by any popular framework to my knowledge, implementing it in Deno would not help projects that want to use CSS Modules, and it doesn't seem to be built for server-side rendering.

https://github.com/WICG/webcomponents/issues/759#issuecomment-520566796

ICSS modules [referring to the pre-existing .module.css CSS Modules] work great with server-side JS, allowing developers to return plain HTML + CSS with no client-side JS required for rendering. I can't see how a CSSStyleSheet [used in CSS Module Scripts] could be used on the server side at all. ICSS modules are popular, and developers have been asking (perhaps naively) for native browser support for "CSS Modules" for years, in the same naive way that they wish for JSX built-in to the browser. This proposal [CSS Module Scripts] is not at all what they meant.

Not that I'm against this being implemented in Deno just because it's not CSS Modules -- it wouldn't get in the way of them -- but I just want to emphasize that it's unclear how it would be used and who would benefit if it was implemented in Deno specifically.

Implementing this would require implementing CSSStyleSheet from the CSS Object Model, which is related to the DOM, and it seems like Deno has mostly treated the DOM as out of scope. I think this combined with the likely lack of a use-case would be a good reason to not implement it in Deno. (If possible, it might be neat to make it so the DOM and CSSOM could be implemented in third-party modules in order to simulate a browser DOM in Deno maybe for testing purposes, and then provide the proper import hooks so CSS Module Scripts could be fully implemented through a third-party module.)

ayame113 commented 1 year ago

I think #13898 explains how CSSStyleSheet is useful in Deno. If CSSStyleSheet is implemented in Deno, it would be convenient to have CSS Modules. +1

timreichen commented 1 year ago

+1

jespertheend commented 1 year ago

I’m using Deno as a test runner for code that normally runs in the browser. So Deno simply not throwing when it encounters a css import assertion would already be enough for that specific use case.

But I realize throwing is probably preferable rather than to have it fail silently with undefined behavior.

ayame113 commented 1 year ago

On a separate note, I'm having trouble type checking code for browsers that have CSS imports.

Currently, I create .d.ts and perform type checking in the following way.

// mod.ts

// @deno-types="./stylesheet.d.ts"
import css from "./style.css";
console.log(css);
// stylesheet.d.ts

/// <reference lib="dom" />
declare const stylesheet: CSSStyleSheet;
export default stylesheet;

With this method, type checking works as expected in the editor,

image

but an error occurs in the deno check command.

> deno check ./mod.ts
error: The import assertion type of "css" is unsupported.
    at file:///C:/Users/

It may take some time to implement the CSSStyleSheet API in the future, but it would be nice if there was a way to do type checking with the deno check command before that.

bartlomieju commented 1 year ago

@dsherret it looks like a deno_graph thing. I think it's reasonable to allow css assertion there. WDYT?

aapoalas commented 1 year ago

Fun related spec issue: https://gist.github.com/peetklecha/a55532165dbd4905aa91bbe59e8b1001

TL;DR: Import assertions will be going back to stage 2 and the syntax will likely change.

jespertheend commented 1 year ago

As a workaround, I ended up adding an entry to Deno's import map. All my css imports were already contained in a single module. So now the import map maps this module to a dummy module that exports empty objects instead:

// styles.js

// @ts-ignore
import mySheet from "./mySheet.css" assert {type: "css"};
export {mySheet};
// denoStyles.js

export const mySheet = {};

importmap.json

{
    "imports": {
        "./path/to/styles.js": "./path/to/denoStyles.js"
    }
}

The browser doesn't have this entry in its import map, so it loads browserStyles.js like before, whereas Deno maps it to denoStyles.js which doesn't cause any errors.

Mqxx commented 1 week ago

Any update on this?