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

feat(remix-dev): add opt-in Vanilla Extract cache #5735

Merged
merged 24 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a1dad78
WIP
markdalgleish Feb 17, 2023
e73ee52
Always use `scanModule` to get module order
markdalgleish Feb 20, 2023
6d91bcd
Handle lock errors, identOption and app dir alias
markdalgleish Feb 21, 2023
3363124
Handle relative static assets
markdalgleish Feb 21, 2023
c962630
Handle root relative imports in VE build
markdalgleish Feb 21, 2023
f9097fd
Cache calls to processVanillaFile
markdalgleish Feb 22, 2023
9b41846
Use consumer's css package, avoid mlly dependency
markdalgleish Feb 22, 2023
d9c75cf
Migrate to new official Vanilla Extract compiler
markdalgleish Mar 6, 2023
a34a706
Merge branch 'dev' into markdalgleish/wip-ve-perf
markdalgleish Mar 6, 2023
e24d4dc
Update to snapshot release, add cache option
markdalgleish Mar 9, 2023
e342f9c
Add changeset
markdalgleish Mar 9, 2023
85484d8
Merge branch 'dev' into markdalgleish/vanilla-extract-cache
markdalgleish Mar 9, 2023
c5d00de
Refactor deterministic build output test
markdalgleish Mar 9, 2023
50d9716
Restore original dependency order
markdalgleish Mar 9, 2023
d6a418e
Fix future flag types
markdalgleish Mar 9, 2023
3930975
Tidy up test
markdalgleish Mar 9, 2023
cb2c7b8
Bump to latest Vanilla Extract snapshot release
markdalgleish Mar 9, 2023
66752ff
Bump Vanilla Extract
markdalgleish Mar 9, 2023
54d1964
Translate root relative watchFile paths
markdalgleish Mar 10, 2023
053e1e3
Merge branch 'dev' into markdalgleish/vanilla-extract-cache
markdalgleish Mar 10, 2023
4c23416
Bump to stable Vanilla Extract versions
markdalgleish Mar 10, 2023
29cdc63
Update changeset
markdalgleish Mar 10, 2023
7d5f1a8
Remove loop from deterministic build tests
markdalgleish Mar 13, 2023
53a7a10
Fix tests
markdalgleish Mar 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/vanilla-extract-cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": minor
---

Add experimental support for Vanilla Extract caching which can be enabled by setting `future.unstable_vanillaExtract: { cache: true }` in `remix.config`. This is considered experimental due to the use of a brand new Vanilla Extract compiler under the hood. Note that in order to use this feature, you must be using at least `v1.10.0` of `@vanilla-extract/css`.
289 changes: 189 additions & 100 deletions integration/deterministic-build-output-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,115 +3,204 @@ import globby from "globby";
import fs from "fs";
import path from "path";

import type { FixtureInit } from "./helpers/create-fixture";
import { createFixtureProject, js, css } from "./helpers/create-fixture";

test("builds deterministically under different paths", async () => {
// This test validates various flavors of remix virtual modules to ensure
// we get identical builds regardless of the parent paths. If a virtual
// module resolves or imports from absolute paths (e.g. via `path.resolve`),
// the build hashes may change even though the output is identical. This
// can cause broken apps (i.e. manifest mismatch) if the server and client
// are built separately.
const testCases: Array<{ name: string; init: FixtureInit }> = [
{
name: "all future flags enabled",
init: {
future: {
unstable_cssModules: true,
unstable_cssSideEffectImports: true,
unstable_postcss: true,
unstable_vanillaExtract: true,
v2_routeConvention: true,
},
files: {
"app/routes/_index.mdx": "# hello world",
"app/routes/foo.tsx": js`
export * from "~/foo/bar.server";
import styles from "~/styles/foo.module.css";
import { vanilla } from "~/styles/vanilla.css";
import "~/styles/side-effect.css";
export default () => <div className={[styles.foo, vanilla].join(' ')}>YAY</div>;
`,
"app/foo/bar.server.ts": "export const meta = () => []",
"app/styles/foo.module.css": css`
.foo {
background-image: url(~/images/foo.svg);
composes: bar from "~/styles/bar.module.css";
composes: baz from "./baz.module.css";
}
`,
"app/styles/bar.module.css": css`
.bar {
background-color: peachpuff;
}
`,
"app/styles/baz.module.css": css`
.baz {
color: coral;
}
`,
"app/images/foo.svg": `
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" fill="coral" />
</svg>
`,
"app/styles/vanilla.css.ts": css`
import { style } from "@vanilla-extract/css";
import { chocolate } from "./chocolate.css";
import imageUrl from "~/images/foo.svg";

// Virtual modules tested:
// * browserRouteModulesPlugin (implicitly tested by root route)
// * cssEntryModulePlugin (implicitly tested by build)
// * cssModulesPlugin (via app/routes/foo.tsx' CSS Modules import)
// * cssSideEffectImportsPlugin (via app/routes/foo.tsx' CSS side-effect import)
// * emptyModulesPlugin (via app/routes/foo.tsx' server import)
// * mdx (via app/routes/_index.mdx)
// * serverAssetsManifestPlugin (implicitly tested by build)
// * serverEntryModulePlugin (implicitly tested by build)
// * serverRouteModulesPlugin (implicitly tested by build)
// * vanillaExtractPlugin (via app/routes/foo.tsx' .css.ts file import)
let init = {
future: {
unstable_cssModules: true,
unstable_cssSideEffectImports: true,
unstable_postcss: true,
unstable_vanillaExtract: true,
v2_routeConvention: true,
},
files: {
"app/routes/_index.mdx": "# hello world",
"app/routes/foo.tsx": js`
export * from "~/foo/bar.server";
import styles from "~/styles/foo.module.css";
import { vanilla } from "~/styles/vanilla.css";
import "~/styles/side-effect.css";
export default () => <div className={[styles.foo, vanilla].join(' ')}>YAY</div>;
`,
"app/foo/bar.server.ts": "export const meta = () => []",
"app/styles/foo.module.css": css`
.foo {
background-image: url(~/images/foo.svg);
composes: bar from "~/styles/bar.module.css";
composes: baz from "./baz.module.css";
}
`,
"app/styles/bar.module.css": css`
.bar {
background-color: peachpuff;
}
`,
"app/styles/baz.module.css": css`
.baz {
color: coral;
}
`,
"app/images/foo.svg": `
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" fill="coral" />
</svg>
`,
"app/styles/vanilla.css.ts": css`
import { style } from "@vanilla-extract/css";
import { chocolate } from "./chocolate.css";
import imageUrl from "~/images/foo.svg";
export const vanilla = style([
chocolate,
{
backgroundImage: [
"url(" + imageUrl + ")",
"url(~/images/foo.svg)",
],
}
]);
`,
"app/styles/chocolate.css.ts": css`
import { style } from "@vanilla-extract/css";

export const vanilla = style([
chocolate,
{
backgroundImage: [
"url(" + imageUrl + ")",
"url(~/images/foo.svg)",
],
export const chocolate = style({
color: "chocolate",
});
`,
"app/styles/side-effect.css": css`
.side-effect {
color: mintcream;
}
`,
},
},
},
{
name: "Vanilla Extract with cache enabled",
init: {
future: {
unstable_cssModules: true,
unstable_cssSideEffectImports: true,
unstable_postcss: true,
unstable_vanillaExtract: { cache: true },
v2_routeConvention: true,
},
files: {
"app/routes/_index.mdx": "# hello world",
"app/routes/foo.tsx": js`
export * from "~/foo/bar.server";
import styles from "~/styles/foo.module.css";
import { vanilla } from "~/styles/vanilla.css";
import "~/styles/side-effect.css";
export default () => <div className={[styles.foo, vanilla].join(' ')}>YAY</div>;
`,
"app/foo/bar.server.ts": "export const meta = () => []",
"app/styles/foo.module.css": css`
.foo {
background-image: url(~/images/foo.svg);
composes: bar from "~/styles/bar.module.css";
composes: baz from "./baz.module.css";
}
`,
"app/styles/bar.module.css": css`
.bar {
background-color: peachpuff;
}
]);
`,
"app/styles/chocolate.css.ts": css`
import { style } from "@vanilla-extract/css";
`,
"app/styles/baz.module.css": css`
.baz {
color: coral;
}
`,
"app/images/foo.svg": `
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" fill="coral" />
</svg>
`,
"app/styles/vanilla.css.ts": css`
import { style } from "@vanilla-extract/css";
import { chocolate } from "./chocolate.css";
import imageUrl from "~/images/foo.svg";

export const chocolate = style({
color: "chocolate",
});
`,
"app/styles/side-effect.css": css`
.side-effect {
color: mintcream;
}
`,
export const vanilla = style([
chocolate,
{
backgroundImage: [
"url(" + imageUrl + ")",
"url(~/images/foo.svg)",
],
}
]);
`,
"app/styles/chocolate.css.ts": css`
import { style } from "@vanilla-extract/css";

export const chocolate = style({
color: "chocolate",
});
`,
"app/styles/side-effect.css": css`
.side-effect {
color: mintcream;
}
`,
},
},
};
let dir1 = await createFixtureProject(init);
let dir2 = await createFixtureProject(init);
},
];

expect(dir1).not.toEqual(dir2);
testCases.forEach((testCase) => {
markdalgleish marked this conversation as resolved.
Show resolved Hide resolved
test.describe(testCase.name, () => {
test("builds deterministically under different paths", async () => {
// This test validates various flavors of remix virtual modules to ensure
// we get identical builds regardless of the parent paths. If a virtual
// module resolves or imports from absolute paths (e.g. via `path.resolve`),
// the build hashes may change even though the output is identical. This
// can cause broken apps (i.e. manifest mismatch) if the server and client
// are built separately.

let files1 = await globby(["build/index.js", "public/build/**/*.{js,css}"], {
cwd: dir1,
});
files1 = files1.sort();
let files2 = await globby(["build/index.js", "public/build/**/*.{js,css}"], {
cwd: dir2,
});
files2 = files2.sort();
// Virtual modules tested:
// * browserRouteModulesPlugin (implicitly tested by root route)
// * cssEntryModulePlugin (implicitly tested by build)
// * cssModulesPlugin (via app/routes/foo.tsx' CSS Modules import)
// * cssSideEffectImportsPlugin (via app/routes/foo.tsx' CSS side-effect import)
// * emptyModulesPlugin (via app/routes/foo.tsx' server import)
// * mdx (via app/routes/_index.mdx)
// * serverAssetsManifestPlugin (implicitly tested by build)
// * serverEntryModulePlugin (implicitly tested by build)
// * serverRouteModulesPlugin (implicitly tested by build)
// * vanillaExtractPlugin (via app/routes/foo.tsx' .css.ts file import)
let dir1 = await createFixtureProject(testCase.init);
let dir2 = await createFixtureProject(testCase.init);

expect(dir1).not.toEqual(dir2);

let files1 = await globby(
["build/index.js", "public/build/**/*.{js,css}"],
{
cwd: dir1,
}
);
files1 = files1.sort();
let files2 = await globby(
["build/index.js", "public/build/**/*.{js,css}"],
{
cwd: dir2,
}
);
files2 = files2.sort();

expect(files1.length).toBeGreaterThan(0);
expect(files1).toEqual(files2);
files1.forEach((file, i) => {
expect(fs.readFileSync(path.join(dir1, file))).toEqual(
fs.readFileSync(path.join(dir2, files2[i]))
);
expect(files1.length).toBeGreaterThan(0);
expect(files1).toEqual(files2);
files1.forEach((file, i) => {
expect(fs.readFileSync(path.join(dir1, file))).toEqual(
fs.readFileSync(path.join(dir2, files2[i]))
);
});
});
});
});
2 changes: 1 addition & 1 deletion integration/helpers/create-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { createRequestHandler as createExpressHandler } from "../../build/node_m

const TMP_DIR = path.join(process.cwd(), ".tmp", "integration");

interface FixtureInit {
export interface FixtureInit {
buildStdio?: Writable;
sourcemap?: boolean;
files?: { [filename: string]: string };
Expand Down
Loading