Skip to content

Commit

Permalink
Update browser runtime (#409)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Jervis authored Oct 12, 2021
1 parent e250423 commit a9c5cb7
Show file tree
Hide file tree
Showing 23 changed files with 219 additions and 123 deletions.
9 changes: 9 additions & 0 deletions .changeset/chilled-goats-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@vanilla-extract/vite-plugin': patch
---

Fix plugin for watch mode.

The vite plugin previously relied on a one to one matching of resolve to load calls, and would clean up the CSS stored in memory after it was loaded.

This isn't true in `--watch` mode, as the same file can be loaded on the rebuild without having to be resolved, which the plugin now handles.
10 changes: 10 additions & 0 deletions .changeset/khaki-taxis-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@vanilla-extract/css': patch
---

Improve the browser runtime dev experience.

The vanilla browser runtime now creates style tags containing the CSS itself, rather than injecting it directly into the CSSOM.

This helps with debugability, as the generated CSS can actually be seen in the devtools.
There are also some new attributes set on the style tags, making it easier to identify the source of each style.
9 changes: 9 additions & 0 deletions .changeset/polite-rings-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@vanilla-extract/vite-plugin': patch
---

Update the "vanilla-extract" devStyleRuntime option.

When using the vanilla browser runtime in vite, it now operates on a new model where a .css.js file is generated, that uses @vanilla-extract/css/injectStyles to add the css to the browser.

This allows for hot reloading of styles, and makes styles a bit easier to debug at dev time (because they're actually visible).
10 changes: 10 additions & 0 deletions .changeset/pretty-carrots-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@vanilla-extract/babel-plugin': patch
'@vanilla-extract/vite-plugin': patch
---

Handle vite 2.6.

As of vite 2.6 and greater, `?used` gets appended to css imports, which causes the file imports to be not what we expected.

This caused mismatching classnames in the vite plugin, and it caused the babel plugin to not add filescopes when it should have.
10 changes: 10 additions & 0 deletions .changeset/strong-zebras-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@vanilla-extract/vite-plugin': patch
---

Automatically optimize deps.

When using the new vanilla browser runtime, the new `injectStyles` dependency gets injected at runtime, so vite can't discover it ahead of time and pre-bundle it.

The plugin will now add the dependency to optimizeDeps if the vanilla runtime is being used so that it's optimized up front.
It also ensures that some relevant vanilla packages are externalised in SSR mode.
7 changes: 7 additions & 0 deletions .changeset/tiny-baboons-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@vanilla-extract/integration': patch
---

Export the fileScope functions.

`stringifyFileScope` and `parseFileScope` are now exported to be used in other packages.
4 changes: 2 additions & 2 deletions packages/babel-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { relative, posix, sep } from 'path';
import { types as t, PluginObj, PluginPass, NodePath } from '@babel/core';
import template from '@babel/template';
import { getPackageInfo } from '@vanilla-extract/integration';
import { cssFileFilter, getPackageInfo } from '@vanilla-extract/integration';

const packageIdentifiers = new Set([
'@vanilla-extract/css',
Expand Down Expand Up @@ -183,7 +183,7 @@ export default function (): PluginObj<Context> {
}

this.isESM = false;
this.isCssFile = /\.css\.(js|ts|jsx|tsx)$/.test(opts.filename);
this.isCssFile = cssFileFilter.test(opts.filename);
this.alreadyCompiled = false;

this.importIdentifiers = new Map();
Expand Down
8 changes: 8 additions & 0 deletions packages/css/injectStyles/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"main": "dist/vanilla-extract-css-injectStyles.cjs.js",
"module": "dist/vanilla-extract-css-injectStyles.esm.js",
"browser": {
"./dist/vanilla-extract-css-injectStyles.cjs.js": "./dist/vanilla-extract-css-injectStyles.browser.cjs.js",
"./dist/vanilla-extract-css-injectStyles.esm.js": "./dist/vanilla-extract-css-injectStyles.browser.esm.js"
}
}
9 changes: 5 additions & 4 deletions packages/css/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"adapter.ts",
"transformCss.ts",
"fileScope.ts",
"injectStyles.ts",
"disableRuntimeStyles.ts"
]
},
Expand All @@ -27,6 +28,7 @@
"/adapter",
"/transformCss",
"/fileScope",
"/injectStyles",
"/disableRuntimeStyles"
],
"repository": {
Expand All @@ -43,12 +45,11 @@
"css-what": "^5.0.1",
"cssesc": "^3.0.0",
"csstype": "^3.0.7",
"dedent": "^0.7.0",
"deep-object-diff": "^1.1.0",
"deepmerge": "^4.2.2"
"deepmerge": "^4.2.2",
"outdent": "^0.8.0"
},
"devDependencies": {
"@types/cssesc": "^3.0.0",
"@types/dedent": "^0.7.0"
"@types/cssesc": "^3.0.0"
}
}
4 changes: 2 additions & 2 deletions packages/css/src/fileScope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import dedent from 'dedent';
import outdent from 'outdent';
import { onEndFileScope } from './adapter';
import type { FileScope } from './types';

Expand Down Expand Up @@ -27,7 +27,7 @@ export function hasFileScope() {
export function getFileScope(): FileScope {
if (fileScopes.length === 0) {
throw new Error(
dedent`
outdent`
Styles were unable to be assigned to a file. This is generally caused by one of the following:
- You may have created styles outside of a '.css.ts' context
Expand Down
30 changes: 30 additions & 0 deletions packages/css/src/injectStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { FileScope } from './types';

const stylesheets: Record<string, HTMLElement> = {};

interface InjectStylesOptions {
fileScope: FileScope;
css: string;
}
export const injectStyles = ({ fileScope, css }: InjectStylesOptions) => {
const fileScopeId = fileScope.packageName
? [fileScope.packageName, fileScope.filePath].join('/')
: fileScope.filePath;

let stylesheet = stylesheets[fileScopeId];

if (!stylesheet) {
const styleEl = document.createElement('style');

if (fileScope.packageName) {
styleEl.setAttribute('data-package', fileScope.packageName);
}

styleEl.setAttribute('data-file', fileScope.filePath);
styleEl.setAttribute('type', 'text/css');
stylesheet = stylesheets[fileScopeId] = styleEl;
document.head.appendChild(styleEl);
}

stylesheet.innerHTML = css;
};
46 changes: 4 additions & 42 deletions packages/css/src/runtimeAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
import type { Adapter, Composition, CSS, FileScope } from './types';
import type { Adapter, Composition, CSS } from './types';
import { injectStyles } from './injectStyles';
import { transformCss } from './transformCss';
import { setAdapterIfNotSet } from './adapter';

const stylesheets: Record<string, CSSStyleSheet> = {};

const localClassNames = new Set<string>();
const composedClassLists: Array<Composition> = [];
let bufferedCSSObjs: Array<CSS> = [];

function getStylesheet({ packageName, filePath }: FileScope) {
const fileScopeId = packageName ? `${packageName}${filePath}` : filePath;
if (stylesheets[fileScopeId]) {
return stylesheets[fileScopeId];
}
const styleEl = document.createElement('style');
document.head.appendChild(styleEl);

if (!styleEl.sheet) {
throw new Error(`Couldn't create stylesheet`);
}
stylesheets[fileScopeId] = styleEl.sheet;

return styleEl.sheet;
}

const browserRuntimeAdapter: Adapter = {
appendCss: (cssObj: CSS) => {
bufferedCSSObjs.push(cssObj);
Expand All @@ -40,30 +23,9 @@ const browserRuntimeAdapter: Adapter = {
localClassNames: Array.from(localClassNames),
composedClassLists,
cssObjs: bufferedCSSObjs,
});
const stylesheet = getStylesheet(fileScope);
const existingRuleCount = stylesheet.cssRules.length;

let ruleIndex = 0;

for (const rule of css) {
try {
if (ruleIndex < existingRuleCount) {
stylesheet.deleteRule(ruleIndex);
}
stylesheet.insertRule(rule, ruleIndex++);
} catch (e) {
console.warn(`Failed to insert rule\n${rule}`);

// insert placeholder rule to keep index count correct
stylesheet.insertRule('.--placeholder-rule--{}', ruleIndex - 1);
}
}
}).join('\n');

// Delete remaining rules
while (ruleIndex < existingRuleCount) {
stylesheet.deleteRule(ruleIndex++);
}
injectStyles({ fileScope, css });

bufferedCSSObjs = [];
},
Expand Down
4 changes: 2 additions & 2 deletions packages/css/src/style.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import cssesc from 'cssesc';
import dedent from 'dedent';
import outdent from 'outdent';
import deepmerge from 'deepmerge';

import type {
Expand Down Expand Up @@ -97,7 +97,7 @@ export function fontFace(rule: FontFaceRule, debugId?: string) {

if ('fontFamily' in rule) {
throw new Error(
dedent`
outdent`
This function creates and returns a hashed font-family name, so the "fontFamily" property should not be provided.
If you'd like to define a globally scoped custom font, you can use the "globalFontFace" function instead.
Expand Down
4 changes: 2 additions & 2 deletions packages/css/src/validateSelector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { parse } from 'css-what';
import cssesc from 'cssesc';
import dedent from 'dedent';
import outdent from 'outdent';

// https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
function escapeRegex(string: string) {
Expand Down Expand Up @@ -53,7 +53,7 @@ export const validateSelector = (selector: string, targetClassName: string) => {
}
} catch (err) {
throw new Error(
dedent`
outdent`
Invalid selector: ${replaceTarget()}
Style selectors must target the '&' character (along with any modifiers), e.g. ${'`${parent} &`'} or ${'`${parent} &:hover`'}.
Expand Down
5 changes: 2 additions & 3 deletions packages/integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@
"dependencies": {
"@vanilla-extract/css": "^1.5.0",
"chalk": "^4.1.1",
"dedent": "^0.7.0",
"esbuild": "^0.11.16",
"eval": "^0.1.6",
"find-up": "^5.0.0",
"javascript-stringify": "^2.0.1",
"lodash": "^4.17.21"
"lodash": "^4.17.21",
"outdent": "^0.8.0"
},
"devDependencies": {
"@types/dedent": "^0.7.0",
"@types/lodash": "^4.14.168"
}
}
6 changes: 5 additions & 1 deletion packages/integration/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export { processVanillaFile } from './processVanillaFile';
export {
processVanillaFile,
parseFileScope,
stringifyFileScope,
} from './processVanillaFile';
export { getSourceFromVirtualCssFile } from './virtualFile';
export { getPackageInfo } from './packageInfo';
export { compile, vanillaExtractFilescopePlugin } from './compile';
Expand Down
19 changes: 14 additions & 5 deletions packages/integration/src/processVanillaFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import { transformCss } from '@vanilla-extract/css/transformCss';
import evalCode from 'eval';
import { stringify } from 'javascript-stringify';
import isPlainObject from 'lodash/isPlainObject';
import dedent from 'dedent';
import outdent from 'outdent';
import { hash } from './hash';

const originalNodeEnv = process.env.NODE_ENV;

function stringifyFileScope({ packageName, filePath }: FileScope): string {
export function stringifyFileScope({
packageName,
filePath,
}: FileScope): string {
return packageName ? `${filePath}$$$${packageName}` : filePath;
}

function parseFileScope(serialisedFileScope: string): FileScope {
export function parseFileScope(serialisedFileScope: string): FileScope {
const [filePath, packageName] = serialisedFileScope.split('$$$');

return {
Expand All @@ -33,6 +36,7 @@ interface ProcessVanillaFileOptions {
fileName: string;
base64Source: string;
fileScope: FileScope;
source: string;
}) => string;
}
export function processVanillaFile({
Expand Down Expand Up @@ -113,7 +117,12 @@ export function processVanillaFile({
}.vanilla.css`;

const virtualCssFilePath = serializeVirtualCssPath
? serializeVirtualCssPath({ fileName, base64Source, fileScope })
? serializeVirtualCssPath({
fileName,
base64Source,
fileScope,
source: css,
})
: `import '${fileName}?source=${base64Source}';`;

cssImports.push(virtualCssFilePath);
Expand Down Expand Up @@ -210,7 +219,7 @@ function stringifyExports(
}
}

throw new Error(dedent`
throw new Error(outdent`
Invalid exports.
You can only export plain objects, arrays, strings, numbers and null/undefined.
Expand Down
3 changes: 2 additions & 1 deletion packages/vite-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"author": "SEEK",
"license": "MIT",
"dependencies": {
"@vanilla-extract/integration": "^1.4.2"
"@vanilla-extract/integration": "^1.4.2",
"outdent": "^0.8.0"
},
"devDependencies": {
"vite": "^2.6.0"
Expand Down
Loading

0 comments on commit a9c5cb7

Please sign in to comment.