diff --git a/.changeset/happy-papayas-travel.md b/.changeset/happy-papayas-travel.md new file mode 100644 index 000000000..9530df178 --- /dev/null +++ b/.changeset/happy-papayas-travel.md @@ -0,0 +1,13 @@ +--- +'@vanilla-extract/integration': minor +--- + +Add a `viteResolve` option to the vite-node compiler + +This allows integrations to provide a Vite `resolve` config to the vite-node compiler, which will be used for resolving imports. These options include [`resolve.alias`], [`resolve.dedupe`], [`resolve.conditions`], [`resolve.mainFields`], [`resolve.extensions`], and others. + +[`resolve.alias`]: https://vitejs.dev/config/shared-options.html#resolve-alias +[`resolve.dedupe`]: https://vitejs.dev/config/shared-options.html#resolve-dedupe +[`resolve.conditions`]: https://vitejs.dev/config/shared-options.html#resolve-conditions +[`resolve.mainFields`]: https://vitejs.dev/config/shared-options.html#resolve-mainfields +[`resolve.extensions`]: https://vitejs.dev/config/shared-options.html#resolve-extensions diff --git a/.changeset/small-emus-do.md b/.changeset/small-emus-do.md new file mode 100644 index 000000000..624516b18 --- /dev/null +++ b/.changeset/small-emus-do.md @@ -0,0 +1,13 @@ +--- +'@vanilla-extract/vite-plugin': patch +--- + +Pass Vite `resolve` config to vite-node compiler + +The plugin passes through the project's Vite `resolve` config to the vite-node compiler, which will be used for resolving imports. These options include [`resolve.alias`], [`resolve.dedupe`], [`resolve.conditions`], [`resolve.mainFields`], [`resolve.extensions`], and others. + +[`resolve.alias`]: https://vitejs.dev/config/shared-options.html#resolve-alias +[`resolve.dedupe`]: https://vitejs.dev/config/shared-options.html#resolve-dedupe +[`resolve.conditions`]: https://vitejs.dev/config/shared-options.html#resolve-conditions +[`resolve.mainFields`]: https://vitejs.dev/config/shared-options.html#resolve-mainfields +[`resolve.extensions`]: https://vitejs.dev/config/shared-options.html#resolve-extensions diff --git a/packages/integration/src/compiler.ts b/packages/integration/src/compiler.ts index 8dffc5d17..511c65fe7 100644 --- a/packages/integration/src/compiler.ts +++ b/packages/integration/src/compiler.ts @@ -49,6 +49,7 @@ const scanModule = (entryModule: ModuleNode, root: string) => { const createViteServer = async ({ root, identifiers, + viteResolve, vitePlugins, }: Required>) => { const pkg = getPackageInfo(root); @@ -67,6 +68,7 @@ const createViteServer = async ({ ssr: { noExternal: true, }, + resolve: viteResolve, plugins: [ { name: 'vanilla-extract-externalize', @@ -184,17 +186,20 @@ export interface CreateCompilerOptions { root: string; cssImportSpecifier?: (filePath: string) => string; identifiers?: IdentifierOption; + viteResolve?: ViteConfig['resolve']; vitePlugins?: ViteConfig['plugins']; } export const createCompiler = ({ root, identifiers = 'debug', cssImportSpecifier = (filePath) => filePath + '.vanilla.css', + viteResolve = {}, vitePlugins = [], }: CreateCompilerOptions): Compiler => { const vitePromise = createViteServer({ root, identifiers, + viteResolve, vitePlugins, }); diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index 63c0d264d..d739ea990 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -118,6 +118,7 @@ export function vanillaExtractPlugin({ root: config.root, identifiers: getIdentOption(), cssImportSpecifier: fileIdToVirtualId, + viteResolve: config.resolve, vitePlugins: userConfig.plugins?.flat().filter( (plugin) => typeof plugin === 'object' && diff --git a/tests/compiler/compiler.vitest.test.ts b/tests/compiler/compiler.vitest.test.ts index fcbb8ed4b..3de0d327b 100644 --- a/tests/compiler/compiler.vitest.test.ts +++ b/tests/compiler/compiler.vitest.test.ts @@ -1,23 +1,23 @@ -import { describe, beforeAll, afterAll, test, expect } from 'vitest'; import path from 'path'; -import { createCompiler } from '@vanilla-extract/integration'; - -function toPosix(filePath: string) { - return filePath.split(path.sep).join(path.posix.sep); -} +import { describe, beforeAll, afterAll, test, expect } from 'vitest'; +import { createCompiler, normalizePath } from '@vanilla-extract/integration'; function getLocalFiles(files: Set) { - const posixDirname = toPosix(__dirname); + const posixDirname = normalizePath(__dirname); return [...files] - .map(toPosix) + .map(normalizePath) .filter((file) => file.startsWith(posixDirname)) .map((file) => file.replace(posixDirname, '')); } describe('compiler', () => { let compilers: Record< - 'default' | 'cssImportSpecifier' | 'shortIdentifiers', + | 'default' + | 'cssImportSpecifier' + | 'shortIdentifiers' + | 'vitePlugins' + | 'viteResolve', ReturnType >; @@ -26,19 +26,49 @@ describe('compiler', () => { default: createCompiler({ root: __dirname, }), - cssImportSpecifier: createCompiler({ root: __dirname, cssImportSpecifier: (filePath) => filePath + '.custom-extension.css', }), - shortIdentifiers: createCompiler({ root: __dirname, identifiers: 'short', }), + vitePlugins: createCompiler({ + root: __dirname, + vitePlugins: [ + { + name: 'test-plugin', + resolveId(id) { + if (id === '~/vars') { + return '\0resolved-vars'; + } + }, + load: (id) => { + if (id === '\0resolved-vars') { + return `export const color = "green"`; + } + }, + }, + ], + }), + viteResolve: createCompiler({ + root: __dirname, + viteResolve: { + alias: { + '@util': path.resolve(__dirname, 'fixtures/vite-config/util'), + }, + }, + }), }; }); + afterAll(async () => { + await Promise.allSettled( + Object.values(compilers).map((compiler) => compiler.close()), + ); + }); + test('absolute paths', async () => { const compiler = compilers.default; @@ -72,7 +102,7 @@ describe('compiler', () => { color: red; }" `); - expect(toPosix(filePath)).toBe( + expect(normalizePath(filePath)).toBe( 'fixtures/class-composition/styles.css.ts', ); })(); @@ -84,7 +114,7 @@ describe('compiler', () => { background: blue; }" `); - expect(toPosix(filePath)).toBe( + expect(normalizePath(filePath)).toBe( 'fixtures/class-composition/shared.css.ts', ); })(); @@ -118,7 +148,7 @@ describe('compiler', () => { color: red; }" `); - expect(toPosix(filePath)).toBe( + expect(normalizePath(filePath)).toBe( 'fixtures/class-composition/styles.css.ts', ); })(); @@ -130,7 +160,7 @@ describe('compiler', () => { background: blue; }" `); - expect(toPosix(filePath)).toBe( + expect(normalizePath(filePath)).toBe( 'fixtures/class-composition/shared.css.ts', ); })(); @@ -164,7 +194,7 @@ describe('compiler', () => { color: red; }" `); - expect(toPosix(filePath)).toBe( + expect(normalizePath(filePath)).toBe( 'fixtures/class-composition/styles.css.ts', ); })(); @@ -176,7 +206,7 @@ describe('compiler', () => { background: blue; }" `); - expect(toPosix(filePath)).toBe( + expect(normalizePath(filePath)).toBe( 'fixtures/class-composition/shared.css.ts', ); })(); @@ -193,7 +223,8 @@ describe('compiler', () => { } expect( - toPosix(error?.message.replace(__dirname, '{{__dirname}}') ?? ''), + // We know `error.message` is defined, and we want make the snapshot consistent across machines + normalizePath(error!.message!.replace(__dirname, '{{__dirname}}')), ).toMatchInlineSnapshot( `"No CSS for file: {{__dirname}}/does-not-exist.css.ts"`, ); @@ -341,9 +372,43 @@ describe('compiler', () => { `); }); - afterAll(async () => { - await Promise.allSettled( - Object.values(compilers).map((compiler) => compiler.close()), - ); + test('Vite plugins', async () => { + const compiler = compilers.vitePlugins; + + const cssPath = path.join(__dirname, 'fixtures/vite-config/plugin.css.ts'); + const output = await compiler.processVanillaFile(cssPath); + const { css } = compiler.getCssForFile(cssPath); + + expect(output.source).toMatchInlineSnapshot(` + "import 'fixtures/vite-config/plugin.css.ts.vanilla.css'; + export var root = 'plugin_root__1e902gk0';" + `); + + expect(css).toMatchInlineSnapshot(` + ".plugin_root__1e902gk0 { + color: green; + }" + `); + }); + + test('Vite resolve', async () => { + const compiler = compilers.viteResolve; + + const cssPath = path.join(__dirname, 'fixtures/vite-config/alias.css.ts'); + const output = await compiler.processVanillaFile(cssPath); + const { css } = compiler.getCssForFile(cssPath); + + expect(output.source).toMatchInlineSnapshot(` + "import 'fixtures/vite-config/util/vars.css.ts.vanilla.css'; + import 'fixtures/vite-config/alias.css.ts.vanilla.css'; + export var root = 'alias_root__ez4dr20';" + `); + + expect(css).toMatchInlineSnapshot(` + ".alias_root__ez4dr20 { + --border__13z1r1g0: 1px solid black; + border: var(--border__13z1r1g0); + }" + `); }); }); diff --git a/tests/compiler/fixtures/vite-config/alias.css.ts b/tests/compiler/fixtures/vite-config/alias.css.ts new file mode 100644 index 000000000..983354f6a --- /dev/null +++ b/tests/compiler/fixtures/vite-config/alias.css.ts @@ -0,0 +1,10 @@ +import { style } from '@vanilla-extract/css'; +// @ts-expect-error aliased path +import { border } from '@util/vars.css'; + +export const root = style({ + vars: { + [border]: '1px solid black', + }, + border, +}); diff --git a/tests/compiler/fixtures/vite-config/plugin.css.ts b/tests/compiler/fixtures/vite-config/plugin.css.ts new file mode 100644 index 000000000..2dcbcd8f6 --- /dev/null +++ b/tests/compiler/fixtures/vite-config/plugin.css.ts @@ -0,0 +1,7 @@ +import { style } from '@vanilla-extract/css'; +// @ts-expect-error virtual module +import { color } from '~/vars'; + +export const root = style({ + color, +}); diff --git a/tests/compiler/fixtures/vite-config/util/vars.css.ts b/tests/compiler/fixtures/vite-config/util/vars.css.ts new file mode 100644 index 000000000..cf7fe0974 --- /dev/null +++ b/tests/compiler/fixtures/vite-config/util/vars.css.ts @@ -0,0 +1,3 @@ +import { createVar } from '@vanilla-extract/css'; + +export const border = createVar();