-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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 style transforms #7
Conversation
const contents = await readFile(filePath, "utf-8"); | ||
|
||
if (!filePath.includes("/pages/") && !filePath.includes("/layouts/")) { | ||
const result = await compileComponent(contents, filePath, { resolve }); | ||
const result = await compileComponent(contents, { compileOptions: { resolve }, filename: filePath, projectRoot }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated this function’s signature to (string, options)
to simplify adding more options to the object (I personally like combining options into objects when possible)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the only downside is that "options" implies optional, and at least in the case of filename it's important it gets passed through everywhere so that we can produce good error messages. I think filename is just as important as the source text itself! But as long as we keep the field required it can be in an object.
.replace(/"$/, '') | ||
.split(' ') | ||
.map((c) => c.trim()) | ||
.forEach((c) => classNames.add(c)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where we grab all CSS classes for a given component. This probably isn’t scoped properly yet for each component yet, but I can revisit that later.
src/transform2.ts
Outdated
walk(ast.css, { | ||
async enter(node) { | ||
if (node.type !== 'Style') return; | ||
const styles = await transformStyle(node, { classNames, fileLoc, fileID }); // TODO: styles needs to go in <head> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the object that gets tossed currently (needs a way to inject back into the <head>
)
7dda210
to
eb783f7
Compare
|
||
let css = ''; | ||
switch (styleType) { | ||
case 'text/css': { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This demands a <style type="[type]">
tag. The idea is to support the following types:
<style type="text/css">
(default;type
isn’t required here)<style type="text/scss">
<style type="text/sass">
<style type="text/postcss">
@@ -179,12 +181,12 @@ function compileScriptSafe(raw: string, loader: 'jsx' | 'tsx'): string { | |||
return code; | |||
} | |||
|
|||
async function convertHmxToJsx(template: string, filename: string, compileOptions: CompileOptions) { | |||
async function convertHmxToJsx(template: string, { compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: the fileID
is needed because that generates CSS Module IDs. It’s the project-relative version of filename
. Read: it should be deterministic.
If we used the full filename
to generate CSS Module IDs, then the IDs wouldn’t be deterministic between users. My code at /Users/drew/Code/pikapkg/hosting-experiments/…
would generate different IDs than if I built a site at C:\Users\drew\hosting-experiments\…
. Ditto for people on Macs. Ditto for CI.
Instead, the fileID
should be consistent across users working in the same project. Any computer working on this project should generate the same consistent IDs on build. Building locally should yield the same code as building in CI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we just generate a hash from the declarations inside of a given .className
, then? This is getting wild but since we control the entire document we could even globally dedupe declarations and generate atomic class names (like Fela) and convert your CSS modules class name to a concatenation of those atomic class names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I’ll look into this more. We can definitely add more optimizations after-the-fact like that. But with people using utility classes and such I want to be very careful about not accidentally hashifying a className that shouldn’t be hashified
src/transform2.ts
Outdated
script, | ||
items, | ||
walk(ast.css, { | ||
async enter(node) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the walker wait for Promises here? It might but usually these kinds of things don't, I think. When we refactor this into more distinct parse, optimize, and codegen steps I think we should have some sort of API that the style optimizer (and others) can block on a "finalization" step. That is assuming the walker is sync.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API for an optimize plugin might be something like this (very much pseudocode):
function optimizeStyles() {
let styleEl;
let stylePromise;
return {
visitors: {
Head: {
exit(node) {
// Inject style at the end.
styleEl = someInjectFunction('style');
}
},
Style: {
enter(node) {
const = node.content.styles;
stylePromise = transformStyle(code ...)
}
}
},
async finalize() {
const styles = await stylePromise;
styleEl.text = styles;
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the walker wait for Promises here?
I don’t think it does, you’re right. I’ll update this to something like your suggestion below
…tructuring Docs fix conflicts and structuring
Grabs styles from a
<style>
tag in an.hmx
component.<head>
Example:
Produces:
(API TBD)
CSS
A string of the styles for this component. Needs to be injected into
<head>
… somehowCSS Modules
A
Map
of which class names need to be injected back into the HTML/JSX. For example: