Skip to content

Commit e23e21f

Browse files
committed
feat: wasm
1 parent b5795b5 commit e23e21f

File tree

12 files changed

+187
-94
lines changed

12 files changed

+187
-94
lines changed

packages/playground/alias/vite.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module.exports = {
77
alias: [
88
{ find: 'fs', replacement: path.resolve(__dirname, 'test.js') },
99
{ find: 'fs-dir', replacement: path.resolve(__dirname, 'dir') },
10-
{ find: 'dep', replacement: 'test-resolve-target' },
10+
{ find: 'dep', replacement: 'test-resolve-linked' },
1111
{
1212
find: /^regex\/(.*)/,
1313
replacement: `${path.resolve(__dirname, 'dir')}/$1`
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { untilUpdated } from '../../testUtils'
2+
3+
test('should work', async () => {
4+
await page.click('.run')
5+
await untilUpdated(() => page.textContent('.result'), 'Wasm result: 42')
6+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<button class="run">Click to run wasm</button>
2+
<div class="result"></div>
3+
4+
<script type="module">
5+
import init from './simple.wasm'
6+
;(async () => {
7+
const { exported_func } = await init({
8+
imports: {
9+
imported_func: (res) => {
10+
document.querySelector('.result').textContent = `Wasm result: ${res}`
11+
}
12+
}
13+
})
14+
document.querySelector('.run').addEventListener('click', exported_func)
15+
})()
16+
</script>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "test-wasm",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"debug": "node --inspect-brk ../../vite/bin/vite"
9+
}
10+
}
78 Bytes
Binary file not shown.
Lines changed: 73 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,20 @@
1-
import chalk from 'chalk'
21
import path from 'path'
32
import { parse as parseUrl } from 'url'
43
import fs, { promises as fsp } from 'fs'
54
import mime from 'mime/lite'
65
import { Plugin } from '../plugin'
76
import { ResolvedConfig } from '../config'
8-
import { createDebugger, cleanUrl } from '../utils'
7+
import { cleanUrl } from '../utils'
98
import { FS_PREFIX } from '../constants'
109
import { PluginContext } from 'rollup'
1110
import MagicString from 'magic-string'
1211

13-
const debug = createDebugger('vite:asset')
14-
1512
export const assetUrlRE = /"__VITE_ASSET__(\w+)(?:__(.*)__)?"/g
1613

17-
export function isPublicFile(url: string, root: string): string | undefined {
18-
// note if the file is in /public, the resolver would have returned it
19-
// as-is so it's not going to be a fully resolved path.
20-
if (!url.startsWith('/')) {
21-
return
22-
}
23-
const publicFile = path.posix.join(root, 'public', cleanUrl(url))
24-
if (fs.existsSync(publicFile)) {
25-
return publicFile
26-
} else {
27-
return
28-
}
29-
}
30-
3114
/**
3215
* Also supports loading plain strings with import text from './foo.txt?raw'
3316
*/
3417
export function assetPlugin(config: ResolvedConfig): Plugin {
35-
const publicIdMap = new Map<string, string>()
36-
3718
return {
3819
name: 'vite:asset',
3920

@@ -43,65 +24,28 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
4324
}
4425
// imports to absolute urls pointing to files in /public
4526
// will fail to resolve in the main resolver. handle them here.
46-
const publicFile = isPublicFile(id, config.root)
27+
const publicFile = checkPublicFile(id, config.root)
4728
if (publicFile) {
48-
publicIdMap.set(id, publicFile)
4929
return id
5030
}
5131
},
5232

5333
async load(id) {
54-
let file = cleanUrl(id)
55-
if (!config.assetsInclude(file)) {
34+
if (!config.assetsInclude(cleanUrl(id))) {
5635
return
5736
}
5837

59-
const publicFile = publicIdMap.get(id)
60-
if (publicFile) {
61-
file = publicFile
62-
}
63-
38+
// raw requests, read from disk
6439
if (/(\?|&)raw\b/.test(id)) {
65-
debug(`[raw] ${chalk.dim(file)}`)
40+
const file = checkPublicFile(id, config.root) || cleanUrl(id)
6641
// raw query, read file and return as string
6742
return `export default ${JSON.stringify(
6843
await fsp.readFile(file, 'utf-8')
6944
)}`
7045
}
7146

72-
debug(`[import] ${chalk.dim(file)}`)
73-
74-
// serve
75-
if (config.command === 'serve') {
76-
let publicPath
77-
if (publicFile) {
78-
// in public dir, keep the url as-is
79-
publicPath = id
80-
} else if (id.startsWith(config.root)) {
81-
// in project root, infer short public path
82-
publicPath = '/' + path.posix.relative(config.root, id)
83-
} else {
84-
// outside of project root, use absolute fs path
85-
// (this is speical handled by the serve static middleware
86-
publicPath = FS_PREFIX + id
87-
}
88-
return `export default ${JSON.stringify(publicPath)}`
89-
}
90-
91-
// build
92-
if (publicFile) {
93-
// in public dir, will be copied over to the same url, but need to
94-
// account for base config
95-
return `export default ${JSON.stringify(
96-
config.build.base + id.slice(1)
97-
)}`
98-
} else {
99-
return `export default ${await registerBuildAssetFromFile(
100-
id,
101-
config,
102-
this
103-
)}`
104-
}
47+
const url = await fileToUrl(id, config, this)
48+
return `export default ${JSON.stringify(url)}`
10549
},
10650

10751
renderChunk(code) {
@@ -130,19 +74,44 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
13074
}
13175
}
13276

133-
export async function registerBuildAsset(
134-
url: string,
135-
importer: string,
77+
export function checkPublicFile(url: string, root: string): string | undefined {
78+
// note if the file is in /public, the resolver would have returned it
79+
// as-is so it's not going to be a fully resolved path.
80+
if (!url.startsWith('/')) {
81+
return
82+
}
83+
const publicFile = path.posix.join(root, 'public', cleanUrl(url))
84+
if (fs.existsSync(publicFile)) {
85+
return publicFile
86+
} else {
87+
return
88+
}
89+
}
90+
91+
export function fileToUrl(
92+
id: string,
13693
config: ResolvedConfig,
137-
pluginContext: PluginContext
138-
): Promise<string> {
139-
if (isPublicFile(url, config.root)) {
140-
return config.build.base + url.slice(1)
94+
ctx: PluginContext
95+
) {
96+
if (config.command === 'serve') {
97+
return fileToDevUrl(id, config)
98+
} else {
99+
return fileToBuiltUrl(id, config, ctx)
141100
}
142-
const file = url.startsWith('/')
143-
? path.join(config.root, url)
144-
: path.join(path.dirname(importer), url)
145-
return registerBuildAssetFromFile(file, config, pluginContext)
101+
}
102+
103+
function fileToDevUrl(id: string, { root }: ResolvedConfig) {
104+
if (checkPublicFile(id, root)) {
105+
// in public dir, keep the url as-is
106+
return id
107+
}
108+
if (id.startsWith(root)) {
109+
// in project root, infer short public path
110+
return '/' + path.posix.relative(root, id)
111+
}
112+
// outside of project root, use absolute fs path
113+
// (this is speical handled by the serve static middleware
114+
return FS_PREFIX + id
146115
}
147116

148117
const assetCache = new WeakMap<ResolvedConfig, Map<string, string>>()
@@ -151,11 +120,16 @@ const assetCache = new WeakMap<ResolvedConfig, Map<string, string>>()
151120
* Register an asset to be emitted as part of the bundle (if necessary)
152121
* and returns the resolved public URL
153122
*/
154-
async function registerBuildAssetFromFile(
123+
async function fileToBuiltUrl(
155124
id: string,
156125
config: ResolvedConfig,
157-
pluginContext: PluginContext
126+
pluginContext: PluginContext,
127+
skipPublicCheck = false
158128
): Promise<string> {
129+
if (!skipPublicCheck && checkPublicFile(id, config.root)) {
130+
return config.build.base + id.slice(1)
131+
}
132+
159133
let cache = assetCache.get(config)
160134
if (!cache) {
161135
cache = new Map()
@@ -178,9 +152,7 @@ async function registerBuildAssetFromFile(
178152
content.length < Number(config.build.assetsInlineLimit)
179153
) {
180154
// base64 inlined as a string
181-
url = JSON.stringify(
182-
`data:${mime.getType(file)};base64,${content.toString('base64')}`
183-
)
155+
url = `data:${mime.getType(file)};base64,${content.toString('base64')}`
184156
} else {
185157
// emit as asset
186158
// rollup supports `import.meta.ROLLUP_FILE_URL_*`, but it generates code
@@ -192,11 +164,30 @@ async function registerBuildAssetFromFile(
192164
type: 'asset',
193165
source: content
194166
})
195-
url = JSON.stringify(
196-
`__VITE_ASSET__${fileId}${postfix ? `__${postfix}__` : ``}`
197-
)
167+
url = `__VITE_ASSET__${fileId}${postfix ? `__${postfix}__` : ``}`
198168
}
199169

200170
cache.set(id, url)
201171
return url
202172
}
173+
174+
export async function urlToBuiltUrl(
175+
url: string,
176+
importer: string,
177+
config: ResolvedConfig,
178+
pluginContext: PluginContext
179+
): Promise<string> {
180+
if (checkPublicFile(url, config.root)) {
181+
return config.build.base + url.slice(1)
182+
}
183+
const file = url.startsWith('/')
184+
? path.join(config.root, url)
185+
: path.join(path.dirname(importer), url)
186+
return fileToBuiltUrl(
187+
file,
188+
config,
189+
pluginContext,
190+
// skip public check since we just did it above
191+
true
192+
)
193+
}

packages/vite/src/node/plugins/css.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import chalk from 'chalk'
1818
import { CLIENT_PUBLIC_PATH } from '../constants'
1919
import { ProcessOptions, Result, Plugin as PostcssPlugin } from 'postcss'
2020
import { ViteDevServer } from '../'
21-
import { assetUrlRE, registerBuildAsset } from './asset'
21+
import { assetUrlRE, urlToBuiltUrl } from './asset'
2222
import { Logger } from '../logger'
2323

2424
// const debug = createDebugger('vite:css')
@@ -119,11 +119,12 @@ export function cssPlugin(config: ResolvedConfig): Plugin {
119119
// account for comments https://github.com/vitejs/vite/issues/426
120120
css = css.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1')
121121
if (cssUrlRE.test(css)) {
122-
css = await rewriteCssUrls(css, (url) => {
122+
css = await rewriteCssUrls(css, async (url) => {
123123
if (isExternalUrl(url) || isDataUrl(url)) {
124124
return url
125125
}
126-
return registerBuildAsset(url, id, config, this)
126+
url = await urlToBuiltUrl(url, id, config, this)
127+
return JSON.stringify(url)
127128
})
128129
}
129130
}

packages/vite/src/node/plugins/html.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
transform
1616
} from '@vue/compiler-dom'
1717
import MagicString from 'magic-string'
18-
import { registerBuildAsset, isPublicFile, assetUrlRE } from './asset'
18+
import { checkPublicFile, assetUrlRE, urlToBuiltUrl } from './asset'
1919
import { isCSSRequest, chunkToEmittedCssFileMap } from './css'
2020

2121
const htmlProxyRE = /\?html-proxy&index=(\d+)\.js$/
@@ -70,7 +70,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
7070
const [preHooks, postHooks] = resolveHtmlTransforms(config.plugins)
7171
const processedHtml = new Map<string, string>()
7272
const isExcludedUrl = (url: string) =>
73-
isExternalUrl(url) || isDataUrl(url) || isPublicFile(url, config.root)
73+
isExternalUrl(url) || isDataUrl(url) || checkPublicFile(url, config.root)
7474

7575
return {
7676
name: 'vite:build-html',
@@ -125,7 +125,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
125125
typeAttr && typeAttr.value && typeAttr.value.content === 'module'
126126

127127
const url = srcAttr && srcAttr.value && srcAttr.value.content
128-
if (url && isPublicFile(url, config.root)) {
128+
if (url && checkPublicFile(url, config.root)) {
129129
// referencing public dir url, prefix with base
130130
s.overwrite(
131131
srcAttr.value!.loc.start.offset,
@@ -171,7 +171,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
171171
} else {
172172
assetUrls.push(p)
173173
}
174-
} else if (isPublicFile(url, config.root)) {
174+
} else if (checkPublicFile(url, config.root)) {
175175
s.overwrite(
176176
p.value.loc.start.offset,
177177
p.value.loc.end.offset,
@@ -201,8 +201,12 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
201201
// references the post-build location.
202202
for (const attr of assetUrls) {
203203
const value = attr.value!
204-
const url = await registerBuildAsset(value.content, id, config, this)
205-
s.overwrite(value.loc.start.offset, value.loc.end.offset, url)
204+
const url = await urlToBuiltUrl(value.content, id, config, this)
205+
s.overwrite(
206+
value.loc.start.offset,
207+
value.loc.end.offset,
208+
JSON.stringify(url)
209+
)
206210
}
207211

208212
// TODO should store the imported entries for each page

packages/vite/src/node/plugins/importsAnalysis.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
} from '../server/hmr'
2525
import { FS_PREFIX, CLIENT_PUBLIC_PATH, DEP_VERSION_RE } from '../constants'
2626
import { ViteDevServer } from '../'
27-
import { isPublicFile } from './asset'
27+
import { checkPublicFile } from './asset'
2828

2929
const isDebug = !!process.env.DEBUG
3030
const debugRewrite = createDebugger('vite:rewrite')
@@ -213,7 +213,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
213213
if (
214214
url.startsWith('/') &&
215215
!config.assetsInclude(cleanUrl(url)) &&
216-
isPublicFile(url, config.root)
216+
checkPublicFile(url, config.root)
217217
) {
218218
throw new Error(
219219
`Cannot import non-asset file ${url} which is inside /public.` +

packages/vite/src/node/plugins/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { cssPlugin, cssPostPlugin } from './css'
99
import { assetPlugin } from './asset'
1010
import { clientInjectionsPlugin } from './clientInjections'
1111
import { htmlPlugin } from './html'
12+
import { wasmPlugin } from './wasm'
1213

1314
export function resolvePlugins(
1415
config: ResolvedConfig,
@@ -27,6 +28,7 @@ export function resolvePlugins(
2728
preferConst: true,
2829
namedExports: true
2930
}),
31+
wasmPlugin(config),
3032
assetPlugin(config),
3133
...normalPlugins,
3234
...postPlugins,

0 commit comments

Comments
 (0)