Skip to content

Commit 69d5c2b

Browse files
committed
docs: update
1 parent 9b1cb98 commit 69d5c2b

File tree

10 files changed

+907
-20
lines changed

10 files changed

+907
-20
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import type { Transform } from 'node:stream'
2+
import { Buffer } from 'node:buffer'
3+
import Vinyl from 'vinyl'
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
import { createPlugins } from '@/bundlers/gulp'
6+
import { createCache } from '@/cache'
7+
8+
const getCompilerContextMock = vi.fn((_options?: unknown) => currentContext)
9+
10+
vi.mock('@/context', () => ({
11+
getCompilerContext: (options?: unknown) => getCompilerContextMock(options),
12+
}))
13+
14+
let currentContext: any
15+
16+
function createFile(path: string, contents: string) {
17+
return new Vinyl({
18+
cwd: '/',
19+
base: '/src',
20+
path,
21+
contents: Buffer.from(contents),
22+
})
23+
}
24+
25+
async function runTransform(transform: Transform, file: Vinyl) {
26+
return await new Promise<Vinyl>((resolve, reject) => {
27+
transform.once('data', resolve)
28+
transform.once('error', reject)
29+
transform.write(file)
30+
transform.end()
31+
})
32+
}
33+
34+
describe('bundlers/gulp createPlugins', () => {
35+
let styleHandler: ReturnType<typeof vi.fn>
36+
let templateHandler: ReturnType<typeof vi.fn>
37+
let jsHandler: ReturnType<typeof vi.fn>
38+
let setMangleRuntimeSet: ReturnType<typeof vi.fn>
39+
let twPatcher: any
40+
41+
beforeEach(() => {
42+
const cache = createCache()
43+
const runtimeSet = new Set(['foo'])
44+
45+
styleHandler = vi.fn(async (source: string) => ({
46+
css: `css:${source}`,
47+
}))
48+
templateHandler = vi.fn(async (source: string) => `tpl:${source}`)
49+
jsHandler = vi.fn(async (source: string) => ({ code: `js:${source}` }))
50+
setMangleRuntimeSet = vi.fn()
51+
twPatcher = {
52+
patch: vi.fn(),
53+
getClassSet: vi.fn(async () => runtimeSet),
54+
getClassSetV3: vi.fn(async () => runtimeSet),
55+
majorVersion: 3,
56+
}
57+
58+
currentContext = {
59+
templateHandler,
60+
styleHandler,
61+
jsHandler,
62+
setMangleRuntimeSet,
63+
cache,
64+
twPatcher,
65+
}
66+
67+
getCompilerContextMock.mockClear()
68+
})
69+
70+
it('processes files and caches results across runs', async () => {
71+
const plugins = createPlugins()
72+
expect(getCompilerContextMock).toHaveBeenCalled()
73+
expect(twPatcher.patch).toHaveBeenCalledTimes(1)
74+
75+
const cssFile = createFile('/src/app.wxss', '.foo { color: red; }')
76+
const processedCss = await runTransform(plugins.transformWxss(), cssFile)
77+
expect(processedCss.contents?.toString()).toBe('css:.foo { color: red; }')
78+
expect(styleHandler).toHaveBeenCalledTimes(1)
79+
expect(setMangleRuntimeSet).toHaveBeenCalledTimes(1)
80+
expect([...setMangleRuntimeSet.mock.calls[0][0]]).toEqual(['foo'])
81+
82+
const cachedCssFile = createFile('/src/app.wxss', '.foo { color: red; }')
83+
const cachedCss = await runTransform(plugins.transformWxss(), cachedCssFile)
84+
expect(styleHandler).toHaveBeenCalledTimes(1)
85+
expect(cachedCss.contents?.toString()).toBe('css:.foo { color: red; }')
86+
87+
// Ensure runtime set is reused for JS handler
88+
const jsFile = createFile('/src/app.js', 'console.log("hi")')
89+
const processedJs = await runTransform(plugins.transformJs(), jsFile)
90+
expect(jsHandler).toHaveBeenCalledTimes(1)
91+
const runtimeSetFromCss = setMangleRuntimeSet.mock.calls[0][0]
92+
expect(jsHandler).toHaveBeenCalledWith('console.log("hi")', runtimeSetFromCss, {})
93+
expect(processedJs.contents?.toString()).toBe('js:console.log("hi")')
94+
95+
const cachedJsFile = createFile('/src/app.js', 'console.log("hi")')
96+
await runTransform(plugins.transformJs(), cachedJsFile)
97+
expect(jsHandler).toHaveBeenCalledTimes(1)
98+
99+
const wxmlFile = createFile('/src/app.wxml', '<view class="foo"></view>')
100+
const processedHtml = await runTransform(plugins.transformWxml(), wxmlFile)
101+
expect(templateHandler).toHaveBeenCalledTimes(1)
102+
expect(processedHtml.contents?.toString()).toBe('tpl:<view class="foo"></view>')
103+
104+
const cachedHtmlFile = createFile('/src/app.wxml', '<view class="foo"></view>')
105+
await runTransform(plugins.transformWxml(), cachedHtmlFile)
106+
expect(templateHandler).toHaveBeenCalledTimes(1)
107+
})
108+
})
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import type { OutputAsset, OutputChunk } from 'rollup'
2+
import type { Plugin, ResolvedConfig, TransformResult } from 'vite'
3+
import { beforeEach, describe, expect, it, vi } from 'vitest'
4+
import { UnifiedViteWeappTailwindcssPlugin } from '@/bundlers/vite'
5+
import { createCache } from '@/cache'
6+
7+
const postcssHtmlTransformMock = vi.hoisted(() => vi.fn(() => ({ postcssPlugin: 'mocked-html-transform' }))) as ReturnType<typeof vi.fn>
8+
vi.mock('@weapp-tailwindcss/postcss/html-transform', () => ({
9+
default: postcssHtmlTransformMock,
10+
}))
11+
12+
const transformUVueMock = vi.hoisted(() => vi.fn((code: string, id: string) => ({ code: `uvue:${id}:${code}` }))) as ReturnType<typeof vi.fn>
13+
vi.mock('@/uni-app-x', () => ({
14+
transformUVue: transformUVueMock,
15+
}))
16+
17+
const getCompilerContextMock = vi.fn(() => currentContext)
18+
vi.mock('@/context', () => ({
19+
getCompilerContext: (options?: unknown) => getCompilerContextMock(options),
20+
}))
21+
22+
type InternalContext = ReturnType<typeof createContext>
23+
24+
let currentContext: InternalContext
25+
26+
function createContext(overrides: Partial<InternalContext> = {}) {
27+
const cache = createCache()
28+
const runtimeSet = new Set(['alpha'])
29+
30+
return {
31+
disabled: false,
32+
onLoad: vi.fn(),
33+
onStart: vi.fn(),
34+
onEnd: vi.fn(),
35+
onUpdate: vi.fn(),
36+
templateHandler: vi.fn(async (code: string) => `tpl:${code}`),
37+
styleHandler: vi.fn(async (code: string) => ({
38+
css: `css:${code}`,
39+
map: {
40+
toJSON: () => ({
41+
version: 3,
42+
file: 'style.css',
43+
sources: ['style.css'],
44+
names: [],
45+
mappings: 'AAAA',
46+
sourcesContent: [code],
47+
}),
48+
},
49+
})),
50+
jsHandler: vi.fn(async (code: string) => ({ code: `js:${code}` })),
51+
mainCssChunkMatcher: vi.fn(() => true),
52+
appType: 'uni-app',
53+
setMangleRuntimeSet: vi.fn(),
54+
cache,
55+
cssMatcher: (file: string) => file.endsWith('.css'),
56+
htmlMatcher: (file: string) => file.endsWith('.wxml'),
57+
jsMatcher: (file: string) => file.endsWith('.js'),
58+
wxsMatcher: () => false,
59+
twPatcher: {
60+
patch: vi.fn(),
61+
getClassSet: vi.fn(async () => runtimeSet),
62+
majorVersion: 3,
63+
extract: vi.fn(async () => ({ classSet: runtimeSet })),
64+
},
65+
uniAppX: undefined as any,
66+
runtimeLoaderPath: undefined,
67+
mainChunkRegex: undefined,
68+
cssEntries: undefined,
69+
customReplaceDictionary: undefined,
70+
...overrides,
71+
}
72+
}
73+
74+
function createRollupAsset(source: string): OutputAsset {
75+
return {
76+
type: 'asset',
77+
fileName: 'index.wxml',
78+
name: undefined,
79+
source,
80+
needsCodeReference: false,
81+
}
82+
}
83+
84+
function createRollupChunk(code: string): OutputChunk {
85+
return {
86+
type: 'chunk',
87+
fileName: 'index.js',
88+
name: 'index',
89+
code,
90+
map: null,
91+
facadeModuleId: null,
92+
moduleIds: [],
93+
modules: {},
94+
imports: [],
95+
exports: [],
96+
dynamicImports: [],
97+
implicitlyLoadedBefore: [],
98+
importedBindings: {},
99+
isEntry: true,
100+
isDynamicEntry: false,
101+
referencedFiles: [],
102+
}
103+
}
104+
105+
describe('bundlers/vite UnifiedViteWeappTailwindcssPlugin', () => {
106+
beforeEach(() => {
107+
currentContext = createContext()
108+
getCompilerContextMock.mockClear()
109+
postcssHtmlTransformMock.mockClear()
110+
transformUVueMock.mockClear()
111+
})
112+
113+
it('generates bundle assets and leverages cache', async () => {
114+
const plugins = UnifiedViteWeappTailwindcssPlugin()
115+
expect(plugins).toBeDefined()
116+
const postPlugin = plugins?.find(plugin => plugin.name === 'weapp-tailwindcss:adaptor:post') as Plugin
117+
expect(postPlugin).toBeTruthy()
118+
expect(currentContext.onLoad).toHaveBeenCalledTimes(1)
119+
expect(currentContext.twPatcher.patch).toHaveBeenCalledTimes(1)
120+
121+
const config = {
122+
css: {
123+
postcss: {
124+
plugins: [
125+
{ postcssPlugin: 'postcss-html-transform' },
126+
{ postcssPlugin: 'other' },
127+
],
128+
},
129+
},
130+
} as unknown as ResolvedConfig
131+
132+
postPlugin.configResolved?.(config)
133+
expect(postcssHtmlTransformMock).toHaveBeenCalledTimes(1)
134+
expect(config.css?.postcss?.plugins?.[0]).toEqual({ postcssPlugin: 'mocked-html-transform' })
135+
136+
const html = '<view class="foo">bar</view>'
137+
const js = 'const demo = 1'
138+
const css = '.foo { color: red; }'
139+
140+
const bundle = {
141+
'index.wxml': createRollupAsset(html),
142+
'index.js': createRollupChunk(js),
143+
'index.css': {
144+
...createRollupAsset(css),
145+
fileName: 'index.css',
146+
},
147+
}
148+
149+
await postPlugin.generateBundle?.({} as any, bundle)
150+
151+
expect(currentContext.onStart).toHaveBeenCalledTimes(1)
152+
expect(currentContext.onEnd).toHaveBeenCalledTimes(1)
153+
expect(currentContext.setMangleRuntimeSet).toHaveBeenCalledTimes(1)
154+
expect([...currentContext.setMangleRuntimeSet.mock.calls[0][0]]).toEqual(['alpha'])
155+
156+
expect(currentContext.templateHandler).toHaveBeenCalledTimes(1)
157+
expect((bundle['index.wxml'] as OutputAsset).source).toBe(`tpl:${html}`)
158+
159+
expect(currentContext.jsHandler).toHaveBeenCalledTimes(1)
160+
expect((bundle['index.js'] as OutputChunk).code).toBe(`js:${js}`)
161+
162+
expect(currentContext.styleHandler).toHaveBeenCalledTimes(1)
163+
expect((bundle['index.css'] as OutputAsset).source).toBe(`css:${css}`)
164+
165+
expect(currentContext.onUpdate).toHaveBeenCalledTimes(3)
166+
167+
const bundleSecondRun = {
168+
'index.wxml': createRollupAsset(html),
169+
'index.js': createRollupChunk(js),
170+
'index.css': {
171+
...createRollupAsset(css),
172+
fileName: 'index.css',
173+
},
174+
}
175+
176+
await postPlugin.generateBundle?.({} as any, bundleSecondRun)
177+
178+
expect(currentContext.templateHandler).toHaveBeenCalledTimes(1)
179+
expect(currentContext.jsHandler).toHaveBeenCalledTimes(1)
180+
expect(currentContext.styleHandler).toHaveBeenCalledTimes(1)
181+
expect(currentContext.setMangleRuntimeSet).toHaveBeenCalledTimes(2)
182+
expect(currentContext.onStart).toHaveBeenCalledTimes(2)
183+
expect(currentContext.onEnd).toHaveBeenCalledTimes(2)
184+
expect(currentContext.onUpdate).toHaveBeenCalledTimes(3)
185+
})
186+
187+
it('returns undefined when disabled', () => {
188+
currentContext = createContext({ disabled: true })
189+
const plugins = UnifiedViteWeappTailwindcssPlugin()
190+
expect(plugins).toBeUndefined()
191+
expect(currentContext.twPatcher.patch).not.toHaveBeenCalled()
192+
})
193+
194+
it('provides uni-app-x specific transforms', async () => {
195+
const runtimeSet = new Set(['uvue'])
196+
currentContext = createContext()
197+
currentContext.uniAppX = { enabled: true }
198+
currentContext.twPatcher = {
199+
patch: vi.fn(),
200+
getClassSet: vi.fn(async () => runtimeSet),
201+
extract: vi.fn(async () => ({ classSet: runtimeSet })),
202+
majorVersion: 4,
203+
}
204+
currentContext.setMangleRuntimeSet = vi.fn()
205+
currentContext.onStart = vi.fn()
206+
currentContext.onEnd = vi.fn()
207+
208+
const plugins = UnifiedViteWeappTailwindcssPlugin()
209+
expect(plugins).toBeDefined()
210+
const cssPlugin = plugins?.find(plugin => plugin.name === 'weapp-tailwindcss:uni-app-x:css') as Plugin
211+
const cssPrePlugin = plugins?.find(plugin => plugin.name === 'weapp-tailwindcss:uni-app-x:css:pre') as Plugin
212+
const nvuePlugin = plugins?.find(plugin => plugin.name === 'weapp-tailwindcss:uni-app-x:nvue') as Plugin
213+
214+
expect(cssPlugin?.transform).toBeTypeOf('function')
215+
expect(cssPrePlugin?.transform).toBeTypeOf('function')
216+
expect(nvuePlugin?.transform).toBeTypeOf('function')
217+
218+
const cssResult = await cssPlugin.transform?.('.foo { color: red; }', 'App.uvue?vue&type=style&index=0') as TransformResult
219+
expect(cssResult?.code).toBe('css:.foo { color: red; }')
220+
expect(cssResult?.map).toBeTruthy()
221+
222+
await nvuePlugin.buildStart?.()
223+
const nvueResult = nvuePlugin.transform?.('console.log("x")', 'App.nvue')
224+
expect(transformUVueMock).toHaveBeenCalledWith('console.log("x")', 'App.nvue', currentContext.jsHandler, runtimeSet)
225+
expect(nvueResult).toEqual({ code: 'uvue:App.nvue:console.log("x")' })
226+
})
227+
})

0 commit comments

Comments
 (0)