Skip to content

Commit

Permalink
fix(rspack): match resource for virtual module (#416)
Browse files Browse the repository at this point in the history
* fix: match resource for virtual module

* fix cr

* lint

* Delete .virtual/external-module

---------

Co-authored-by: Kevin Deng 三咲智子 <[email protected]>
  • Loading branch information
ahabhgk and sxzz authored Sep 11, 2024
1 parent e56b48e commit 636de4a
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 38 deletions.
40 changes: 17 additions & 23 deletions src/rspack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
UnpluginInstance,
} from '../types'
import { createBuildContext, normalizeMessage } from './context'
import { decodeVirtualModuleId, encodeVirtualModuleId } from './utils'
import { FakeVirtualModulesPlugin, decodeVirtualModuleId, encodeVirtualModuleId, isVirtualModuleId } from './utils'

const TRANSFORM_LOADER = resolve(
__dirname,
Expand All @@ -22,16 +22,16 @@ const LOAD_LOADER = resolve(
__DEV__ ? '../../dist/rspack/loaders/load.js' : 'rspack/loaders/load',
)

const VIRTUAL_MODULE_PATH = resolve(__dirname, __DEV__ ? '../../dist/rspack/virtual.js' : 'rspack/virtual.js')
const VIRTUAL_MODULE_QUERY_PREFIX = '?unplugin_rspack_virtual='
const VIRTUAL_MODULE_PREFIX = VIRTUAL_MODULE_PATH + VIRTUAL_MODULE_QUERY_PREFIX

export function getRspackPlugin<UserOptions = Record<string, never>>(
factory: UnpluginFactory<UserOptions>,
): UnpluginInstance<UserOptions>['rspack'] {
return (userOptions?: UserOptions): RspackPluginInstance => {
return {
apply(compiler) {
// We need the prefix of virtual modules to be an absolute path so rspack let's us load them (even if it's made up)
// In the loader we strip the made up prefix path again
const VIRTUAL_MODULE_PREFIX = resolve(compiler.options.context ?? process.cwd(), 'node_modules/.virtual')

const injected = compiler.$unpluginContext || {}
compiler.$unpluginContext = injected

Expand Down Expand Up @@ -66,6 +66,10 @@ export function getRspackPlugin<UserOptions = Record<string, never>>(

// resolveId hook
if (plugin.resolveId) {
const vfs = new FakeVirtualModulesPlugin(plugin)
vfs.apply(compiler)
plugin.__vfsModules = new Set()

compiler.hooks.compilation.tap(plugin.name, (compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.resolve.tapPromise(plugin.name, async (resolveData) => {
const id = normalizeAbsolutePath(resolveData.request)
Expand Down Expand Up @@ -102,8 +106,13 @@ export function getRspackPlugin<UserOptions = Record<string, never>>(

// If the resolved module does not exist,
// we treat it as a virtual module
if (!fs.existsSync(resolved))
if (!fs.existsSync(resolved)) {
if (!plugin.__vfsModules!.has(resolved)) {
plugin.__vfsModules!.add(resolved)
await vfs.writeModule(resolved)
}
resolved = encodeVirtualModuleId(resolved, plugin)
}

resolveData.request = resolved
})
Expand All @@ -115,23 +124,8 @@ export function getRspackPlugin<UserOptions = Record<string, never>>(
compiler.options.module.rules.unshift({
enforce: plugin.enforce,
include(id) {
// always return true for virtual module, filter it in resourceQuery
if (id === VIRTUAL_MODULE_PATH)
return true

// load include filter
if (plugin.loadInclude && !plugin.loadInclude(id))
return false

// Don't run load hook for external modules
return !externalModules.has(id)
},
resourceQuery(query) {
if (!query.startsWith(VIRTUAL_MODULE_QUERY_PREFIX))
return true

// filter the decoded virtual module id
const id = decodeVirtualModuleId(VIRTUAL_MODULE_PATH + query, plugin)
if (isVirtualModuleId(id, plugin))
id = decodeVirtualModuleId(id, plugin)

// load include filter
if (plugin.loadInclude && !plugin.loadInclude(id))
Expand Down
34 changes: 30 additions & 4 deletions src/rspack/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,39 @@
import fs from 'fs'
import { basename, dirname, resolve } from 'path'
import type { Compiler } from '@rspack/core'
import type { ResolvedUnpluginOptions } from '../types'

export function encodeVirtualModuleId(id: string, plugin: ResolvedUnpluginOptions): string {
return plugin.__virtualModulePrefix + encodeURIComponent(id)
return resolve(plugin.__virtualModulePrefix, encodeURIComponent(id))
}

export function decodeVirtualModuleId(encoded: string, plugin: ResolvedUnpluginOptions): string {
return decodeURIComponent(encoded.slice(plugin.__virtualModulePrefix.length))
export function decodeVirtualModuleId(encoded: string, _plugin: ResolvedUnpluginOptions): string {
return decodeURIComponent(basename(encoded))
}

export function isVirtualModuleId(encoded: string, plugin: ResolvedUnpluginOptions): boolean {
return encoded.startsWith(plugin.__virtualModulePrefix)
return dirname(encoded) === plugin.__virtualModulePrefix
}

export class FakeVirtualModulesPlugin {
name = 'FakeVirtualModulesPlugin'
constructor(private plugin: ResolvedUnpluginOptions) {}

apply(compiler: Compiler) {
const dir = this.plugin.__virtualModulePrefix
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
compiler.hooks.shutdown.tap(this.name, () => {
if (fs.existsSync(dir)) {
fs.rmdirSync(dir, { recursive: true })
}
})
}

async writeModule(file: string) {
const path = encodeVirtualModuleId(file, this.plugin)
await fs.promises.writeFile(path, '')
return path
}
}
2 changes: 0 additions & 2 deletions src/rspack/virtual.js

This file was deleted.

9 changes: 0 additions & 9 deletions tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Options } from 'tsup'
import { copy } from 'esbuild-plugin-copy'
import Unused from 'unplugin-unused/esbuild'

export const tsup: Options = {
Expand All @@ -26,14 +25,6 @@ export const tsup: Options = {
__DEV__: 'false',
},
esbuildPlugins: [
copy({
assets: [
{
from: ['./src/rspack/virtual.js'],
to: ['./rspack/virtual.js'],
},
],
}),
Unused({ level: 'error' }),
],
}

0 comments on commit 636de4a

Please sign in to comment.