linaria icon indicating copy to clipboard operation
linaria copied to clipboard

feat(solid): initial draft implementation

Open raveclassic opened this issue 3 years ago • 22 comments

Motivation

This PR introduces an initial implementation of linaria processor for solid.

Summary

The idea is to completely eliminate any extra runtime components and transpile the code into intermediate representation suitable for further transpilation with solid (this means, the processor doesn't transpile the code down to solid's output, it should be the responsibility of the end developer to pipe transformations correctly: first goes @linaria/solid, then the code must be transformed by solid with either babel plugin, vite plugin etc.)

Test plan

The tests are not working at the moment as it turns out it's quite hard to write any meaningful tests without the actual transpilation step. Testing snapshots seems too fragile and unmaintainable.

Instead I tried to take an approach on transpiling the code of tests with linaria+solid in a custom jest transform. It works but I'd still like to avoid testing stringified (or jsonified) output. Instead, it would be really cool to make use of JSDom to run the output, render generated components and test interaction with them. E.g. a reactive prop on a component must update components runtime structure etc.

At the moment I'm able to transpile the code with both jest transform (seamlessly transpiles the whole test and we can use regular TSX to write the code - huge win) and with a custom transpile step (takes a stringified input and transpiles down to solid's output - not so good as we lose TSX and still need to eval the code somehow).

Another thing to consider is that jest transform throws away generated css and I'd like to render it with JSDom so that interactions with components can be fully tested (e.g. when we set a prop on a component, component's color turns blue). But this would require adding a build step with esbuild/vite/etc and somehow integrate it into jest+jsdom - the first thing that comes to mind is a custom jest environment based on jest-environment-jsdom, this might work. Also we could take a look at @testing-library/react and see what they are doing and try to implement the same things in the jest environment.

All these turned out to be much more work, so I stopped on that as unfortunately I don't have enough time at the moment.

Still, I'm opening the PR to demonstrate possible implementation of processor, as @Anber suggested in DM 👌

raveclassic avatar Oct 23 '22 08:10 raveclassic

⚠️ No Changeset found

Latest commit: b03f7338bf739d5d32b90a43f1fd76e06ffda81b

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

changeset-bot[bot] avatar Oct 23 '22 08:10 changeset-bot[bot]

Снимок экрана 2022-10-23 в 14 45 18

Our implementation

usmanyunusov avatar Oct 23 '22 11:10 usmanyunusov

Work in progress on this thread?

usmanyunusov avatar Nov 04 '22 17:11 usmanyunusov

@usmanyunusov yeah, in progress but I have very little time now to work on it.

raveclassic avatar Nov 25 '22 13:11 raveclassic

@Anber smth weird is happening to test snapshots in interop package, I have newlines removed in my output but they are expected in snapshots. This is weird since the repo is using pnpm and there should be no package version collisions. image

What's interesting is that it's fine on master branch 🤔

raveclassic avatar Nov 25 '22 14:11 raveclassic

I don't know why, but adding "@babel/core": "7.18.9" (which is the same exact version as in the repo root) to devDependencies in /packages/solid/package.json updates babel's transitive dependencies 🤷‍♂️ image

raveclassic avatar Nov 25 '22 15:11 raveclassic

@Anber https://github.com/pnpm/pnpm/issues/4868

raveclassic avatar Nov 25 '22 15:11 raveclassic

Ok I was finally able to fix this. Seems running pnpm i with manually added dependencies in package.json is quite different from running pnpm add -D in the package directory. So, pnpm add -D @babel/[email protected] solid-js@^1.6.2 fixes the issue (note there're no ~/^ on @babel/types dependency). But this breaks syncpack, so I had to tweak pnpm-lockfile.yml manually and add ^ there and in package.json.

raveclassic avatar Nov 25 '22 16:11 raveclassic

I also dropped all tests in favor of adding a real solid example in /examples directory. This way we could combine tests with examples and not mess a lot with jsdom.

raveclassic avatar Nov 25 '22 16:11 raveclassic

ok, I finished the basic example built with solid-js, vite, @linaria/vite, and vite-plugin-solid, it's available at /examples/solid, the command to run is the regular pnpm start

raveclassic avatar Nov 25 '22 17:11 raveclassic

@Anber ready for review

raveclassic avatar Nov 25 '22 19:11 raveclassic

@Anber thanks for enabling the CI on this PR 🙏 ~there's something weird, I'm also on Node v16.16.0, but CI says it can't install node typings https://github.com/callstack/linaria/actions/runs/3554400920/jobs/5970646566#step:7:369 But they are there, and locally everything is fine 🤷‍♂️~ No, they are missing, adding them in a moment

raveclassic avatar Nov 26 '22 16:11 raveclassic

🙏

usmanyunusov avatar Nov 26 '22 20:11 usmanyunusov

when can i expect this feature release?

Tetrax-10 avatar Dec 16 '22 09:12 Tetrax-10

I just tested it quickly and don't fully understand solid, please correct me if I'm wrong.
This seems to lose reactivity on props in some cases:

const HelloWorldSolid = () => {
    const [count, setCount] = createSignal(0);

    return (
        <div>
            <Heading style={{"margin-top": `${count()}px`}}>
                count: {count()}
            </Heading>
            <button onClick={() => setCount((c) => c + 1)}> increment</button>
        </div>
    );
};
const Heading = styled.h1`
  background: red;
`;

The text in the heading updates correctly, but the margin is not applied.
If you replace Heading with h1 it works.

edit: By quickly checking the transform, it looks like getStyleConstantValueExpression does unwrap props.style which leads to loss of reactivity. Perhaps wrapping the declaration in createMemo would propagate reactivity, but there might be more performant ways to handle it.

shiro avatar Dec 21 '22 02:12 shiro

Is this PR abandoned?

NightmanCZ90 avatar Feb 25 '23 12:02 NightmanCZ90

What's up with this PR?

ivancuric avatar May 09 '23 14:05 ivancuric

just use tw/unocss + attributify much better dx: framework/language agnostic, shorter then inlining css, no need to write style merging logic for every component, just spread props.

MrFoxPro avatar Mar 12 '24 21:03 MrFoxPro

just use tw/unocss + attributify much better dx: framework/language agnostic, shorter then inlining css, no need to write style merging logic for every component, just spread props.

This is just another DSL. I am looking for a way to write actual CSS, not tailwind shorthands in made up attributes that may or may not clash with actual JSX props or HTML attributes

ivancuric avatar Mar 13 '24 07:03 ivancuric

just use tw/unocss + attributify much better dx: framework/language agnostic, shorter then inlining css, no need to write style merging logic for every component, just spread props.

This is just another DSL. I am looking for a way to write actual CSS, not tailwind shorthands in made up attributes that may or may not clash with actual JSX props or HTML attributes

There are some drawbacks, but I think it's much better then using linaria. You don't need this rocket-science euristic compilier that slows down dev builds and potentially breaks your code.

You can start with simple pure class="". Or you can even try web components, so you will also co-locate your css with components. These are much better options for any projects in long-term run, because that tools are much easier to work with and maintain, also they're language-agnostic. Linaria, on the other hand, has strong legacy vibes.

I spent year with unocss after linaria and it feels so much better to work with.

MrFoxPro avatar Mar 13 '24 07:03 MrFoxPro

@MrFoxPro let's keep this PR focused on linaria and solid. This is not the right place to promote failwind or any other framework.

raveclassic avatar Mar 13 '24 09:03 raveclassic

@Tetrax-10 @NightmanCZ90 @ivancuric I see all your comments here but unfortunately I don't have spare time to work on this. Your are kindly welcome to take on the initiative here 🙏

raveclassic avatar Mar 13 '24 09:03 raveclassic