From 42082dd1d0b01430ed2fdc0d71ba9609fb96a495 Mon Sep 17 00:00:00 2001 From: KCM Date: Sun, 25 Jan 2026 12:44:02 -0600 Subject: [PATCH 1/3] fix: vanilla extract import triggers declaration mode sidecar. --- docs/loader.md | 12 ++++++++++++ docs/plugin.md | 19 +++++++++++++++++- docs/type-generation.md | 3 ++- package-lock.json | 4 ++-- packages/css/package.json | 2 +- packages/css/src/generateTypes.ts | 16 +++++++++++++-- packages/css/test/generateTypes.test.ts | 26 ++++++++++++++++++++++++- packages/playwright/package.json | 2 +- 8 files changed, 75 insertions(+), 9 deletions(-) diff --git a/docs/loader.md b/docs/loader.md index 6201fab..0c6db24 100644 --- a/docs/loader.md +++ b/docs/loader.md @@ -2,6 +2,18 @@ `@knighted/css/loader` lets bundlers attach compiled CSS strings to any module by appending the `?knighted-css` query when importing. The loader mirrors the module graph, compiles every CSS dialect it discovers (CSS, Sass, Less, vanilla-extract, etc.), and exposes the concatenated result as `knightedCss`. +## Loader vs bridge (quick comparison) + +Use this table to decide which loader you need before wiring up rules: + +| Capability | @knighted/css/loader | @knighted/css/loader-bridge | +| ------------------------ | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | +| Input | Original JS/TS module source + its style imports | Compiled CSS Modules output (post-hash) | +| CSS extraction | Yes (walks the import graph) | No (wraps upstream output) | +| Export behavior | Appends `knightedCss` (and optional selector exports) onto the original module | Exposes `knightedCss`/`knightedCssModules` only; does **not** re-export JS/TS module exports | +| When to use | Default choice for `?knighted-css` in JS/TS modules | When you need hashed CSS Modules output for runtime `knightedCss` | +| Combined wrapper needed? | Only for explicit `?knighted-css&combined` usage | **Yes** if you still need original JS/TS exports (use `&combined` via the resolver plugin) | + ## Loader example ```ts diff --git a/docs/plugin.md b/docs/plugin.md index 5d79537..0789740 100644 --- a/docs/plugin.md +++ b/docs/plugin.md @@ -57,7 +57,9 @@ type KnightedCssResolverPluginOptions = { `.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, `.cts`, `.mjs`, `.cjs`. - `debug` (optional): Logs rewrite decisions and a summary of cache hits/misses. - `combinedPaths` (optional): List of strings or regexes. Any resolved path that matches will - receive `&combined` alongside `?knighted-css`. + receive `&combined` alongside `?knighted-css`. Use this when the request is handled by + `@knighted/css/loader-bridge` (hashed CSS Modules) or whenever you need a wrapper module + that re-exports the original JS/TS exports plus `knightedCss`. - `strictSidecar` (optional): When true, only modules present in the manifest are rewritten. Defaults to true when `manifestPath` is provided. - `manifestPath` (optional): Path to the sidecar manifest generated by @@ -118,6 +120,21 @@ export default { If you have modules that are consumed with combined exports (`?knighted-css&combined`), set `combinedPaths` to ensure the resolver appends `&combined` during rewrites. +`&combined` is required when the underlying loader chain does **not** preserve the module’s +original exports. The common case is declaration mode + `--hashed`, where requests flow through +`@knighted/css/loader-bridge` and would otherwise only expose `knightedCss`/`knightedCssModules`. +Appending `&combined` tells the loader to generate a small wrapper module that re-exports the +original module and then appends the `knightedCss` exports. + +You typically **do not** need `combinedPaths` when requests are handled by `@knighted/css/loader` +(non-hashed declaration mode), because that loader appends exports directly onto the original +module. + +Outside of loader-bridge, `&combined` is still useful when you are **not** running +`knighted-css-generate-types` but want a single runtime import that includes both the original +JS/TS exports and `knightedCss` (for example, in runtime-only builds, tests, or tooling that +cannot resolve the generated `.knighted-css` proxy modules). + Example of an import that relies on `&combined` at runtime: ```ts diff --git a/docs/type-generation.md b/docs/type-generation.md index aa7d04a..82b7fc1 100644 --- a/docs/type-generation.md +++ b/docs/type-generation.md @@ -94,7 +94,8 @@ The CLI only emits `.d.ts` sidecars for files that pass all of the following che - **Within the project root**: if a candidate file resolves outside `--root`, the CLI skips it and logs a warning. - **Imports styles**: the file must import/require a style resource directly (e.g. `.css`, - `.scss`, `.sass`, `.less`) or resolve to one via tsconfig paths / resolver hooks. + `.scss`, `.sass`, `.less`, or vanilla-extract `.css.ts` / `.css.js`) or resolve to one via + tsconfig paths / resolver hooks. - **Produces selectors**: the extracted CSS must be non-empty and yield at least one selector token; otherwise the sidecar is skipped. diff --git a/package-lock.json b/package-lock.json index 6309d5f..15e9df4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11437,7 +11437,7 @@ }, "packages/css": { "name": "@knighted/css", - "version": "1.2.0-rc.0", + "version": "1.2.0-rc.1", "license": "MIT", "dependencies": { "es-module-lexer": "^2.0.0", @@ -11709,7 +11709,7 @@ "name": "@knighted/css-playwright-fixture", "version": "0.0.0", "dependencies": { - "@knighted/css": "1.2.0-rc.0", + "@knighted/css": "1.2.0-rc.1", "@knighted/jsx": "^1.7.5", "lit": "^3.2.1", "react": "^19.0.0", diff --git a/packages/css/package.json b/packages/css/package.json index 0e82898..173a6bf 100644 --- a/packages/css/package.json +++ b/packages/css/package.json @@ -1,6 +1,6 @@ { "name": "@knighted/css", - "version": "1.2.0-rc.0", + "version": "1.2.0-rc.1", "description": "A build-time utility that traverses JavaScript/TypeScript module dependency graphs to extract, compile, and optimize all imported CSS into a single, in-memory string.", "type": "module", "main": "./dist/css.js", diff --git a/packages/css/src/generateTypes.ts b/packages/css/src/generateTypes.ts index 2f32344..a58cbeb 100644 --- a/packages/css/src/generateTypes.ts +++ b/packages/css/src/generateTypes.ts @@ -1061,6 +1061,18 @@ function isStyleResource(filePath: string): boolean { return STYLE_EXTENSIONS.some(ext => normalized.endsWith(ext)) } +function isVanillaExtractResource(filePath: string): boolean { + const normalized = filePath.toLowerCase() + return ( + normalized.endsWith('.css.ts') || + normalized.endsWith('.css.js') || + normalized.endsWith('.css.mts') || + normalized.endsWith('.css.cts') || + normalized.endsWith('.css.mjs') || + normalized.endsWith('.css.cjs') + ) +} + function isCssModuleResource(filePath: string): boolean { return /\.module\.(css|scss|sass|less)$/i.test(filePath) } @@ -1116,7 +1128,7 @@ async function hasStyleImports( if (!resource) { continue } - if (isStyleResource(resource)) { + if (isStyleResource(resource) || isVanillaExtractResource(resource)) { return true } const resolved = await resolveImportPath( @@ -1128,7 +1140,7 @@ async function hasStyleImports( options.resolverFactory, RESOLUTION_EXTENSIONS, ) - if (resolved && isStyleResource(resolved)) { + if (resolved && (isStyleResource(resolved) || isVanillaExtractResource(resolved))) { return true } } diff --git a/packages/css/test/generateTypes.test.ts b/packages/css/test/generateTypes.test.ts index cf4ead6..2c0070c 100644 --- a/packages/css/test/generateTypes.test.ts +++ b/packages/css/test/generateTypes.test.ts @@ -27,6 +27,7 @@ const { resolveIndexFallback, readManifest, writeSidecarManifest, + hasStyleImports, } = __generateTypesInternals let cachedCliSnapshots: Record | null = null @@ -390,6 +391,25 @@ test('generateTypes declaration mode skips files without style imports', async ( } }) +test('hasStyleImports treats vanilla extract modules as style imports', async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'knighted-vanilla-')) + try { + const srcDir = path.join(root, 'src') + await fs.mkdir(srcDir, { recursive: true }) + await fs.writeFile( + path.join(srcDir, 'styles.css.ts'), + "export const theme = { color: 'rebeccapurple' }\n", + ) + const entryPath = path.join(srcDir, 'entry.ts') + await fs.writeFile(entryPath, "import './styles.css.ts'\n") + + const hasStyles = await hasStyleImports(entryPath, { rootDir: root }) + assert.equal(hasStyles, true) + } finally { + await fs.rm(root, { recursive: true, force: true }) + } +}) + test('generateTypes hashed emits selector proxies for modules', async () => { const project = await setupFixtureProject() try { @@ -616,7 +636,11 @@ test('generateTypes resolves hash-imports workspace package.json imports', async const sassModuleDir = path.join(appRoot, 'node_modules', 'sass') await fs.mkdir(path.dirname(sassModuleDir), { recursive: true }) try { - await fs.symlink(sassPackageDir, sassModuleDir) + await fs.symlink( + sassPackageDir, + sassModuleDir, + process.platform === 'win32' ? 'junction' : 'dir', + ) } catch { await fs.cp(sassPackageDir, sassModuleDir, { recursive: true }) } diff --git a/packages/playwright/package.json b/packages/playwright/package.json index e01ce7f..fb7882f 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -36,7 +36,7 @@ "pretest": "npm run types && npm run build" }, "dependencies": { - "@knighted/css": "1.2.0-rc.0", + "@knighted/css": "1.2.0-rc.1", "@knighted/jsx": "^1.7.5", "lit": "^3.2.1", "react": "^19.0.0", From c7893eacad0be52aa848deb62cc3024c1c1c76a5 Mon Sep 17 00:00:00 2001 From: KCM Date: Sun, 25 Jan 2026 13:21:11 -0600 Subject: [PATCH 2/3] test: ve sidecar end-to-end. --- packages/playwright/rspack.mode.config.js | 12 ++++ packages/playwright/src/mode/constants.ts | 4 ++ .../src/mode/declaration/vanilla-card.css.ts | 15 +++++ .../src/mode/declaration/vanilla-card.tsx | 21 ++++++ .../src/mode/declaration/vanilla-host.ts | 64 +++++++++++++++++++ packages/playwright/src/mode/index.ts | 25 ++++++++ packages/playwright/test/mode.spec.ts | 19 ++++++ 7 files changed, 160 insertions(+) create mode 100644 packages/playwright/src/mode/declaration/vanilla-card.css.ts create mode 100644 packages/playwright/src/mode/declaration/vanilla-card.tsx create mode 100644 packages/playwright/src/mode/declaration/vanilla-host.ts diff --git a/packages/playwright/rspack.mode.config.js b/packages/playwright/rspack.mode.config.js index 7590fd0..a6da78e 100644 --- a/packages/playwright/rspack.mode.config.js +++ b/packages/playwright/rspack.mode.config.js @@ -33,6 +33,18 @@ export default async () => ({ }, module: { rules: [ + { + test: /\.css\.ts$/, + type: 'javascript/auto', + use: [ + { + loader: '@knighted/css/loader', + options: { + vanilla: { transformToEsm: true }, + }, + }, + ], + }, { test: /\.module\.css$/, include: declarationHashedDir, diff --git a/packages/playwright/src/mode/constants.ts b/packages/playwright/src/mode/constants.ts index 5267d73..8714e98 100644 --- a/packages/playwright/src/mode/constants.ts +++ b/packages/playwright/src/mode/constants.ts @@ -1,5 +1,6 @@ export const MODE_MODULE_HOST_TAG = 'knighted-mode-module-host' export const MODE_DECL_HOST_TAG = 'knighted-mode-declaration-host' +export const MODE_DECL_VANILLA_HOST_TAG = 'knighted-mode-declaration-vanilla-host' export const MODE_DECL_HASHED_HOST_TAG = 'knighted-mode-declaration-hashed-host' export const MODE_DECL_STABLE_HOST_TAG = 'knighted-mode-declaration-stable-host' @@ -10,11 +11,13 @@ export const MODE_DECL_STRICT_SKIP_PROBE_TEST_ID = 'mode-declaration-strict-skip export const MODE_MODULE_HOST_TEST_ID = 'mode-module-host' export const MODE_DECL_HOST_TEST_ID = 'mode-declaration-host' +export const MODE_DECL_VANILLA_HOST_TEST_ID = 'mode-declaration-vanilla-host' export const MODE_DECL_HASHED_HOST_TEST_ID = 'mode-declaration-hashed-host' export const MODE_DECL_STABLE_HOST_TEST_ID = 'mode-declaration-stable-host' export const MODE_MODULE_LIGHT_TEST_ID = 'mode-module-light' export const MODE_DECL_LIGHT_TEST_ID = 'mode-declaration-light' +export const MODE_DECL_VANILLA_LIGHT_TEST_ID = 'mode-declaration-vanilla-light' export const MODE_DECL_HASHED_LIGHT_TEST_ID = 'mode-declaration-hashed-light' export const MODE_DECL_STABLE_LIGHT_TEST_ID = 'mode-declaration-stable-light' @@ -24,5 +27,6 @@ export const MODE_DECL_STABLE_SELECTOR_TEST_ID = 'mode-declaration-stable-select export const MODE_MODULE_SHADOW_TEST_ID = 'mode-module-shadow' export const MODE_DECL_SHADOW_TEST_ID = 'mode-declaration-shadow' +export const MODE_DECL_VANILLA_SHADOW_TEST_ID = 'mode-declaration-vanilla-shadow' export const MODE_DECL_HASHED_SHADOW_TEST_ID = 'mode-declaration-hashed-shadow' export const MODE_DECL_STABLE_SHADOW_TEST_ID = 'mode-declaration-stable-shadow' diff --git a/packages/playwright/src/mode/declaration/vanilla-card.css.ts b/packages/playwright/src/mode/declaration/vanilla-card.css.ts new file mode 100644 index 0000000..edb57be --- /dev/null +++ b/packages/playwright/src/mode/declaration/vanilla-card.css.ts @@ -0,0 +1,15 @@ +import { globalStyle, style } from '@vanilla-extract/css' + +export const vanillaCardClass = style({ + background: 'rgb(14, 116, 144)', + color: 'rgb(236, 254, 255)', + borderRadius: '24px', +}) + +globalStyle('[data-testid="mode-declaration-vanilla-light"]', { + boxShadow: '0 18px 40px rgba(14, 116, 144, 0.35)', +}) + +globalStyle('[data-testid="mode-declaration-vanilla-shadow"]', { + boxShadow: '0 18px 40px rgba(14, 116, 144, 0.35)', +}) diff --git a/packages/playwright/src/mode/declaration/vanilla-card.tsx b/packages/playwright/src/mode/declaration/vanilla-card.tsx new file mode 100644 index 0000000..d441714 --- /dev/null +++ b/packages/playwright/src/mode/declaration/vanilla-card.tsx @@ -0,0 +1,21 @@ +import '../mode.css' +import { vanillaCardClass } from './vanilla-card.css.js' + +type DeclarationVanillaCardProps = { + label: string + testId: string +} + +export function DeclarationVanillaCard({ label, testId }: DeclarationVanillaCardProps) { + return ( +
+

+ Declaration vanilla-extract +

+

{label}

+
+ ) +} diff --git a/packages/playwright/src/mode/declaration/vanilla-host.ts b/packages/playwright/src/mode/declaration/vanilla-host.ts new file mode 100644 index 0000000..e741ba1 --- /dev/null +++ b/packages/playwright/src/mode/declaration/vanilla-host.ts @@ -0,0 +1,64 @@ +import { reactJsx } from '@knighted/jsx/react' +import { createRoot, type Root } from 'react-dom/client' +import { LitElement, css, html, unsafeCSS } from 'lit' + +import { + DeclarationVanillaCard, + knightedCss as declarationVanillaCss, +} from './vanilla-card.js' +import { + MODE_DECL_VANILLA_HOST_TAG, + MODE_DECL_VANILLA_SHADOW_TEST_ID, +} from '../constants.js' + +const hostShell = css` + :host { + display: block; + padding: 1.5rem; + border-radius: 1.5rem; + background: rgb(15, 23, 42); + box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.2); + } +` + +export class ModeDeclarationVanillaHost extends LitElement { + static styles = [hostShell, unsafeCSS(declarationVanillaCss)] + #reactRoot?: Root + + firstUpdated(): void { + this.#mountReact() + } + + disconnectedCallback(): void { + this.#reactRoot?.unmount() + super.disconnectedCallback() + } + + #mountReact(): void { + if (!this.#reactRoot) { + const outlet = this.renderRoot.querySelector( + '[data-react-root]', + ) as HTMLDivElement | null + if (!outlet) return + this.#reactRoot = createRoot(outlet) + } + this.#renderReactTree() + } + + #renderReactTree(): void { + if (!this.#reactRoot) return + this.#reactRoot.render( + reactJsx`<${DeclarationVanillaCard} label="Shadow DOM" testId=${MODE_DECL_VANILLA_SHADOW_TEST_ID} />`, + ) + } + + render() { + return html`
` + } +} + +export function ensureModeDeclarationVanillaHostDefined(): void { + if (!customElements.get(MODE_DECL_VANILLA_HOST_TAG)) { + customElements.define(MODE_DECL_VANILLA_HOST_TAG, ModeDeclarationVanillaHost) + } +} diff --git a/packages/playwright/src/mode/index.ts b/packages/playwright/src/mode/index.ts index 6266eb1..5058af2 100644 --- a/packages/playwright/src/mode/index.ts +++ b/packages/playwright/src/mode/index.ts @@ -4,6 +4,10 @@ import type { JSX } from 'react' import { ModuleCard } from './module/module-card.js' import { DeclarationCard } from './declaration/declaration-card.js' +import { + DeclarationVanillaCard, + knightedCss as declarationVanillaCss, +} from './declaration/vanilla-card.js' import { DeclarationHashedCard, selectors as declarationHashedSelectors, @@ -19,7 +23,10 @@ import { MODE_DECL_HOST_TEST_ID, MODE_DECL_HASHED_HOST_TAG, MODE_DECL_HASHED_HOST_TEST_ID, + MODE_DECL_VANILLA_HOST_TAG, + MODE_DECL_VANILLA_HOST_TEST_ID, MODE_DECL_LIGHT_TEST_ID, + MODE_DECL_VANILLA_LIGHT_TEST_ID, MODE_DECL_STABLE_HOST_TAG, MODE_DECL_STABLE_HOST_TEST_ID, MODE_MODULE_HOST_TAG, @@ -36,6 +43,7 @@ import { } from './constants.js' import { ensureModeModuleHostDefined } from './module/host.js' import { ensureModeDeclarationHostDefined } from './declaration/host.js' +import { ensureModeDeclarationVanillaHostDefined } from './declaration/vanilla-host.js' import { ensureModeDeclarationHashedHostDefined } from './declaration-hashed/host.js' import { ensureModeDeclarationStableHostDefined } from './declaration-stable/host.js' @@ -59,6 +67,12 @@ export function renderModeDemo(): HTMLElement { renderSection(root, 'module', ModuleCard, MODE_MODULE_LIGHT_TEST_ID) renderSection(root, 'declaration', DeclarationCard, MODE_DECL_LIGHT_TEST_ID) + renderSection( + root, + 'declaration-vanilla', + DeclarationVanillaCard, + MODE_DECL_VANILLA_LIGHT_TEST_ID, + ) renderSection( root, 'declaration-hashed', @@ -128,6 +142,17 @@ export function renderModeDemo(): HTMLElement { declarationHost.setAttribute('data-testid', MODE_DECL_HOST_TEST_ID) root.appendChild(declarationHost) + const vanillaStyle = document.createElement('style') + vanillaStyle.setAttribute('data-mode', 'declaration-vanilla') + vanillaStyle.textContent = declarationVanillaCss + const headTarget = document.head ?? root + headTarget.appendChild(vanillaStyle) + + ensureModeDeclarationVanillaHostDefined() + const declarationVanillaHost = document.createElement(MODE_DECL_VANILLA_HOST_TAG) + declarationVanillaHost.setAttribute('data-testid', MODE_DECL_VANILLA_HOST_TEST_ID) + root.appendChild(declarationVanillaHost) + ensureModeDeclarationHashedHostDefined() const declarationHashedHost = document.createElement(MODE_DECL_HASHED_HOST_TAG) declarationHashedHost.setAttribute('data-testid', MODE_DECL_HASHED_HOST_TEST_ID) diff --git a/packages/playwright/test/mode.spec.ts b/packages/playwright/test/mode.spec.ts index a93f23b..f1cee42 100644 --- a/packages/playwright/test/mode.spec.ts +++ b/packages/playwright/test/mode.spec.ts @@ -7,6 +7,9 @@ import { MODE_DECL_HASHED_SHADOW_TEST_ID, MODE_DECL_HASHED_LIGHT_TEST_ID, MODE_DECL_LIGHT_TEST_ID, + MODE_DECL_VANILLA_HOST_TEST_ID, + MODE_DECL_VANILLA_LIGHT_TEST_ID, + MODE_DECL_VANILLA_SHADOW_TEST_ID, MODE_DECL_STABLE_SELECTOR_TEST_ID, MODE_DECL_STABLE_HOST_TEST_ID, MODE_DECL_STABLE_SHADOW_TEST_ID, @@ -114,6 +117,22 @@ test.describe('mode resolver fixture', () => { expect(shadowMetrics.borderRadius).toBe(lightMetrics.borderRadius) }) + test('declaration vanilla-extract light and shadow styles match', async ({ page }) => { + const lightCard = page.getByTestId(MODE_DECL_VANILLA_LIGHT_TEST_ID) + await expect(lightCard).toBeVisible() + const lightMetrics = await readMetrics(lightCard) + + const shadowMetrics = await readShadowMetrics( + page, + MODE_DECL_VANILLA_HOST_TEST_ID, + MODE_DECL_VANILLA_SHADOW_TEST_ID, + ) + + expect(shadowMetrics.background).toBe(lightMetrics.background) + expect(shadowMetrics.color).toBe(lightMetrics.color) + expect(shadowMetrics.borderRadius).toBe(lightMetrics.borderRadius) + }) + test('declaration hashed selectors are hashed', async ({ page }) => { const probe = page.getByTestId(MODE_DECL_HASHED_SELECTOR_TEST_ID) await expect(probe).toHaveAttribute('data-selector', /.+/) From d6bd8a9c5fa8fb0e07cbec566dd2c955e26f5256 Mon Sep 17 00:00:00 2001 From: KCM Date: Sun, 25 Jan 2026 14:02:15 -0600 Subject: [PATCH 3/3] test: increase patch. --- package.json | 1 + packages/css/test/generateTypes.test.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/package.json b/package.json index 6f34908..2ff962e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "engineStrict": true, "scripts": { "build": "npm run build -w @knighted/css", + "prepare": "husky", "test": "npm run test -w @knighted/css", "pretest": "npm run build", "test:e2e": "npm run test -w @knighted/css-playwright-fixture", diff --git a/packages/css/test/generateTypes.test.ts b/packages/css/test/generateTypes.test.ts index 2c0070c..00e9e67 100644 --- a/packages/css/test/generateTypes.test.ts +++ b/packages/css/test/generateTypes.test.ts @@ -410,6 +410,27 @@ test('hasStyleImports treats vanilla extract modules as style imports', async () } }) +test('hasStyleImports treats vanilla extract extension variants as style imports', async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), 'knighted-vanilla-variants-')) + try { + const srcDir = path.join(root, 'src') + await fs.mkdir(srcDir, { recursive: true }) + const entryPath = path.join(srcDir, 'entry.ts') + const variants = ['.css.js', '.css.mjs', '.css.cjs', '.css.mts', '.css.cts'] + + for (const ext of variants) { + const stylePath = path.join(srcDir, `styles${ext}`) + await fs.writeFile(stylePath, '') + await fs.writeFile(entryPath, `import './styles${ext}'\n`) + + const hasStyles = await hasStyleImports(entryPath, { rootDir: root }) + assert.equal(hasStyles, true) + } + } finally { + await fs.rm(root, { recursive: true, force: true }) + } +}) + test('generateTypes hashed emits selector proxies for modules', async () => { const project = await setupFixtureProject() try {