Merge 249ff138e3 into fa81d697fe
This commit is contained in:
commit
4d3f2353d6
2 changed files with 166 additions and 1 deletions
137
integrations/vite/yarn-pnp.test.ts
Normal file
137
integrations/vite/yarn-pnp.test.ts
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { candidate, css, html, js, test, ts, yaml } from '../utils'
|
||||
|
||||
const DIST = path.resolve(import.meta.dirname, '../../dist')
|
||||
|
||||
const resolutions = Object.fromEntries(
|
||||
fs
|
||||
.readdirSync(DIST)
|
||||
.filter((filename) => filename.endsWith('.tgz'))
|
||||
.map((filename) => {
|
||||
let name =
|
||||
filename === 'tailwindcss.tgz'
|
||||
? 'tailwindcss'
|
||||
: `@tailwindcss/${filename.slice('tailwindcss-'.length, -'.tgz'.length)}`
|
||||
|
||||
let tarball = path.join(DIST, filename).replaceAll('\\', '/')
|
||||
return [name, `file:${tarball}`]
|
||||
}),
|
||||
)
|
||||
|
||||
test(
|
||||
'Yarn PnP scans a physical workspace package through its virtual peer-dependency path',
|
||||
{
|
||||
installDependencies: false,
|
||||
timeout: 240_000,
|
||||
fs: {
|
||||
'package.json': JSON.stringify(
|
||||
{
|
||||
private: true,
|
||||
packageManager: 'yarn@4.18.0',
|
||||
workspaces: ['packages/*'],
|
||||
resolutions,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'.yarnrc.yml': yaml`
|
||||
#
|
||||
nodeLinker: pnp
|
||||
enableGlobalCache: false
|
||||
`,
|
||||
'packages/app/package.json': JSON.stringify(
|
||||
{
|
||||
name: 'app',
|
||||
private: true,
|
||||
type: 'module',
|
||||
dependencies: {
|
||||
dep: 'workspace:*',
|
||||
react: '19.2.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'@tailwindcss/vite': 'workspace:^',
|
||||
tailwindcss: 'workspace:^',
|
||||
vite: '^7',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'packages/app/index.html': html`
|
||||
<head>
|
||||
<link rel="stylesheet" href="/src/index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="twp:p-10"></div>
|
||||
</body>
|
||||
`,
|
||||
'packages/app/src/index.css': css` @import 'dep/style.css'; `,
|
||||
'packages/app/vite.config.ts': ts`
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
build: { cssMinify: false },
|
||||
plugins: [tailwindcss()],
|
||||
})
|
||||
`,
|
||||
'packages/dep/package.json': JSON.stringify(
|
||||
{
|
||||
name: 'dep',
|
||||
type: 'module',
|
||||
exports: {
|
||||
'./style.css': './style.css',
|
||||
},
|
||||
devDependencies: {
|
||||
tailwindcss: 'workspace:^',
|
||||
},
|
||||
peerDependencies: {
|
||||
react: '>=18',
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
'packages/dep/style.css': css`
|
||||
@import 'tailwindcss' prefix(twp) source(none);
|
||||
@plugin './plugin.js';
|
||||
@source './src';
|
||||
@source not './src/excluded.ts';
|
||||
`,
|
||||
'packages/dep/plugin.js': js`
|
||||
export default function ({ addUtilities }) {
|
||||
addUtilities({
|
||||
'.from-virtual-plugin': {
|
||||
display: 'block',
|
||||
},
|
||||
})
|
||||
}
|
||||
`,
|
||||
'packages/dep/src/index.ts': ts` export let classes = 'twp:flex twp:from-virtual-plugin' `,
|
||||
'packages/dep/src/excluded.ts': ts` export let classes = 'twp:hidden' `,
|
||||
},
|
||||
},
|
||||
async ({ root, exec, fs, expect }) => {
|
||||
await exec('corepack yarn install')
|
||||
|
||||
let resolution = await exec(
|
||||
`corepack yarn workspace app node -e "const p=require('pnpapi');const v=p.resolveToUnqualified('dep',process.cwd()+'/');console.log('VIRTUAL='+JSON.stringify({virtual:v.includes('__virtual__'),resolved:p.resolveVirtual(v)}))"`,
|
||||
)
|
||||
let virtual = JSON.parse(resolution.match(/VIRTUAL=(.*)/)![1])
|
||||
expect(virtual.virtual).toBe(true)
|
||||
expect(virtual.resolved).toBe(path.join(root, 'packages/dep') + path.sep)
|
||||
|
||||
await exec('corepack yarn workspace app vite build')
|
||||
|
||||
let files = await fs.glob('packages/app/dist/**/*.css')
|
||||
expect(files).toHaveLength(1)
|
||||
let [filename] = files[0]
|
||||
|
||||
await fs.expectFileToContain(filename, [
|
||||
candidate`twp:flex`,
|
||||
candidate`twp:from-virtual-plugin`,
|
||||
])
|
||||
await fs.expectFileNotToContain(filename, [candidate`twp:hidden`, candidate`twp:p-10`])
|
||||
},
|
||||
)
|
||||
|
|
@ -10,6 +10,7 @@ import {
|
|||
import { clearRequireCache } from '@tailwindcss/node/require-cache'
|
||||
import { Scanner } from '@tailwindcss/oxide'
|
||||
import fs from 'node:fs/promises'
|
||||
import Module from 'node:module'
|
||||
import path from 'node:path'
|
||||
import type { Environment, InternalResolveOptions, Plugin, ResolvedConfig } from 'vite'
|
||||
import * as vite from 'vite'
|
||||
|
|
@ -308,6 +309,28 @@ function idToPath(id: string) {
|
|||
return path.resolve(id.replace(/\?.*$/, ''))
|
||||
}
|
||||
|
||||
type PnpApi = {
|
||||
resolveVirtual?: (path: string) => string | null
|
||||
}
|
||||
|
||||
function devirtualizeScannerSourceBase(base: string) {
|
||||
let findPnpApi = (
|
||||
Module as typeof Module & {
|
||||
findPnpApi?: (lookupSource: string) => PnpApi | null
|
||||
}
|
||||
).findPnpApi
|
||||
|
||||
let resolved = findPnpApi?.(base)?.resolveVirtual?.(base)
|
||||
|
||||
// Yarn's JavaScript filesystem layer can access virtual package paths, but
|
||||
// the native scanner cannot. Keep the original path everywhere else and
|
||||
// only devirtualize the base handed to the filesystem scanner. Paths inside
|
||||
// ZIP archives are still virtual filesystems, so leave those untouched.
|
||||
return resolved && !resolved.split(/[\\/]/).some((segment) => segment.endsWith('.zip'))
|
||||
? resolved
|
||||
: base
|
||||
}
|
||||
|
||||
/**
|
||||
* A Map that can generate default values for keys that don't exist.
|
||||
* Generated default values are added to the map to avoid recomputation.
|
||||
|
|
@ -431,7 +454,12 @@ class Root {
|
|||
return [{ ...this.compiler.root, negated: false }]
|
||||
})().concat(this.compiler.sources)
|
||||
|
||||
this.scanner = new Scanner({ sources })
|
||||
let scannerSources = sources.map((source) => {
|
||||
let base = devirtualizeScannerSourceBase(source.base)
|
||||
return { ...source, base }
|
||||
})
|
||||
|
||||
this.scanner = new Scanner({ sources: scannerSources })
|
||||
DEBUG && I.end('Setup scanner')
|
||||
} else {
|
||||
for (let buildDependency of this.buildDependencies.keys()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue