Skip to content

Commit c80f9ab

Browse files
authoredDec 3, 2024··
fix(farm): serialized communication between js and rust (#442)
* chore: Optimize serialization issues between js and rust * chore: update virtual module * chore: optimize farm * chore: remove unless code * chore: update farm * chore: bump farm version * chore: update resolvedPaths * chore: bump farm version * chore: remove unless code * chore: optimize code * chore: update farm deps * chore: update code * chore: optimize code * chore: update type return annotation * chore: optimize code * chore: optimize code
1 parent f2f1ce4 commit c80f9ab

File tree

4 files changed

+251
-105
lines changed

4 files changed

+251
-105
lines changed
 

‎package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
"@ampproject/remapping": "^2.3.0",
5050
"@antfu/eslint-config": "^3.11.2",
5151
"@antfu/ni": "^0.23.1",
52-
"@farmfe/cli": "1.0.3",
53-
"@farmfe/core": "1.3.12",
52+
"@farmfe/cli": "^1.0.4",
53+
"@farmfe/core": "^1.4.6",
5454
"@rspack/cli": "^1.1.4",
5555
"@rspack/core": "^1.1.4",
5656
"@types/fs-extra": "^11.0.4",

‎pnpm-lock.yaml

+58-60
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/farm/index.ts

+60-36
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,25 @@ import type {
1515
UnpluginOptions,
1616
} from '../types'
1717
import type { JsPluginExtended, WatchChangeEvents } from './utils'
18-
import path from 'path'
18+
19+
import { existsSync } from 'node:fs'
20+
import path from 'node:path'
21+
1922
import { toArray } from '../utils/general'
2023
import { createFarmContext, unpluginContext } from './context'
21-
2224
import {
25+
appendQuery,
2326
convertEnforceToPriority,
2427
convertWatchEventChange,
2528
customParseQueryString,
29+
decodeStr,
30+
encodeStr,
2631
getContentValue,
2732
guessIdLoader,
2833
isObject,
34+
isStartsWithSlash,
2935
isString,
30-
transformQuery,
36+
removeQuery,
3137
} from './utils'
3238

3339
export function getFarmPlugin<
@@ -97,30 +103,46 @@ export function toFarmPlugin(plugin: UnpluginOptions, options?: Record<string, a
97103
const farmContext = createFarmContext(context!, resolvedIdPath)
98104
const resolveIdResult = await _resolveId.call(
99105
Object.assign(unpluginContext(context), farmContext),
100-
params.source,
106+
decodeStr(params.source),
101107
resolvedIdPath ?? null,
102108
{ isEntry },
103109
)
104110

105111
if (isString(resolveIdResult)) {
106112
return {
107-
resolvedPath: resolveIdResult,
113+
resolvedPath: removeQuery(encodeStr(resolveIdResult)),
108114
query: customParseQueryString(resolveIdResult),
109-
sideEffects: false,
115+
sideEffects: true,
110116
external: false,
111117
meta: {},
112118
}
113119
}
114-
else if (isObject(resolveIdResult)) {
120+
if (isObject(resolveIdResult)) {
121+
return {
122+
resolvedPath: removeQuery(encodeStr(resolveIdResult?.id)),
123+
query: customParseQueryString(resolveIdResult?.id),
124+
sideEffects: false,
125+
external: Boolean(resolveIdResult?.external),
126+
meta: {},
127+
}
128+
}
129+
if (!isStartsWithSlash(params.source))
130+
return null
131+
132+
const rootAbsolutePath = path.resolve(
133+
params.source,
134+
)
135+
if (
136+
existsSync(rootAbsolutePath)
137+
) {
115138
return {
116-
resolvedPath: resolveIdResult?.id,
117-
query: customParseQueryString(resolveIdResult!.id),
139+
resolvedPath: removeQuery(encodeStr(rootAbsolutePath)),
140+
query: customParseQueryString(rootAbsolutePath),
118141
sideEffects: false,
119-
external: resolveIdResult?.external,
142+
external: false,
120143
meta: {},
121144
}
122145
}
123-
return null
124146
},
125147
} as unknown as JsPlugin['resolve']
126148
}
@@ -132,27 +154,34 @@ export function toFarmPlugin(plugin: UnpluginOptions, options?: Record<string, a
132154
resolvedPaths: ['.*'],
133155
},
134156
async executor(
135-
id: PluginLoadHookParam,
157+
params: PluginLoadHookParam,
136158
context,
137159
): Promise<PluginLoadHookResult | null> {
138-
if (plugin.loadInclude && !plugin.loadInclude(id.resolvedPath))
139-
return null
140-
const loader = guessIdLoader(id.resolvedPath)
160+
const resolvedPath = decodeStr(params.resolvedPath)
161+
162+
const id = appendQuery(resolvedPath, params.query)
163+
164+
const loader = guessIdLoader(resolvedPath)
165+
141166
const shouldLoadInclude
142-
= plugin.loadInclude && plugin.loadInclude(id.resolvedPath)
143-
const farmContext = createFarmContext(context!, id.resolvedPath)
167+
= plugin.loadInclude?.(id)
168+
169+
if (!shouldLoadInclude)
170+
return null
171+
172+
const farmContext = createFarmContext(context!, id)
173+
144174
const content: TransformResult = await _load.call(
145175
Object.assign(unpluginContext(context!), farmContext),
146-
id.resolvedPath,
176+
id,
147177
)
178+
148179
const loadFarmResult: PluginLoadHookResult = {
149180
content: getContentValue(content),
150181
moduleType: loader,
151182
}
152-
if (shouldLoadInclude)
153-
return loadFarmResult
154183

155-
return null
184+
return loadFarmResult
156185
},
157186
} as JsPlugin['load']
158187
}
@@ -165,35 +194,30 @@ export function toFarmPlugin(plugin: UnpluginOptions, options?: Record<string, a
165194
params: PluginTransformHookParam,
166195
context: CompilationContext,
167196
) {
168-
if (params.query.length)
169-
transformQuery(params)
197+
const resolvedPath = decodeStr(params.resolvedPath)
170198

171-
if (
172-
plugin.transformInclude
173-
&& !plugin.transformInclude(params.resolvedPath)
174-
) {
175-
return null
176-
}
199+
const id = appendQuery(resolvedPath, params.query)
177200

178201
const loader = params.moduleType ?? guessIdLoader(params.resolvedPath)
202+
179203
const shouldTransformInclude
180-
= plugin.transformInclude
181-
&& plugin.transformInclude(params.resolvedPath)
182-
const farmContext = createFarmContext(context, params.resolvedPath)
204+
= plugin.transformInclude?.(id)
205+
const farmContext = createFarmContext(context, id)
206+
207+
if (!shouldTransformInclude)
208+
return null
209+
183210
const resource: TransformResult = await _transform.call(
184211
Object.assign(unpluginContext(context), farmContext),
185212
params.content,
186-
params.resolvedPath,
213+
id,
187214
)
188-
189215
if (resource && typeof resource !== 'string') {
190216
const transformFarmResult: PluginTransformHookResult = {
191217
content: getContentValue(resource),
192218
moduleType: loader,
193219
sourceMap: JSON.stringify(resource.map),
194220
}
195-
if (shouldTransformInclude)
196-
return transformFarmResult
197221

198222
return transformFarmResult
199223
}

‎src/farm/utils.ts

+131-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { JsPlugin } from '@farmfe/core'
2-
import type { TransformResult } from '../types'
3-
import path from 'path'
4-
import * as querystring from 'querystring'
2+
3+
import path from 'node:path'
4+
import * as querystring from 'node:querystring'
55

66
export type WatchChangeEvents = 'create' | 'update' | 'delete'
77

@@ -59,10 +59,6 @@ export function convertWatchEventChange(
5959
return watchEventChange[value]
6060
}
6161

62-
export function getContentValue(content: TransformResult): string {
63-
return typeof content === 'string' ? content : content!.code
64-
}
65-
6662
export function isString(variable: unknown): variable is string {
6763
return typeof variable === 'string'
6864
}
@@ -86,6 +82,134 @@ export function customParseQueryString(url: string | null): [string, string][] {
8682
return paramsArray
8783
}
8884

85+
export function encodeStr(str: string): string {
86+
const len = str.length
87+
if (len === 0)
88+
return str
89+
90+
const firstNullIndex = str.indexOf('\0')
91+
if (firstNullIndex === -1)
92+
return str
93+
94+
const result = Array.from({ length: len + countNulls(str, firstNullIndex) })
95+
96+
let pos = 0
97+
for (let i = 0; i < firstNullIndex; i++) {
98+
result[pos++] = str[i]
99+
}
100+
101+
for (let i = firstNullIndex; i < len; i++) {
102+
const char = str[i]
103+
if (char === '\0') {
104+
result[pos++] = '\\'
105+
result[pos++] = '0'
106+
}
107+
else {
108+
result[pos++] = char
109+
}
110+
}
111+
112+
return path.posix.normalize(result.join(''))
113+
}
114+
115+
export function decodeStr(str: string): string {
116+
const len = str.length
117+
if (len === 0)
118+
return str
119+
120+
const firstIndex = str.indexOf('\\0')
121+
if (firstIndex === -1)
122+
return str
123+
124+
const result = Array.from({ length: len - countBackslashZeros(str, firstIndex) })
125+
126+
let pos = 0
127+
for (let i = 0; i < firstIndex; i++) {
128+
result[pos++] = str[i]
129+
}
130+
131+
let i = firstIndex
132+
while (i < len) {
133+
if (str[i] === '\\' && str[i + 1] === '0') {
134+
result[pos++] = '\0'
135+
i += 2
136+
}
137+
else {
138+
result[pos++] = str[i++]
139+
}
140+
}
141+
142+
return path.posix.normalize(result.join(''))
143+
}
144+
145+
export function getContentValue(content: any): string {
146+
if (content === null || content === undefined) {
147+
throw new Error('Content cannot be null or undefined')
148+
}
149+
150+
const strContent = typeof content === 'string'
151+
? content
152+
: (content.code || '')
153+
154+
return encodeStr(strContent)
155+
}
156+
157+
function countNulls(str: string, startIndex: number): number {
158+
let count = 0
159+
const len = str.length
160+
for (let i = startIndex; i < len; i++) {
161+
if (str[i] === '\0')
162+
count++
163+
}
164+
return count
165+
}
166+
167+
function countBackslashZeros(str: string, startIndex: number): number {
168+
let count = 0
169+
const len = str.length
170+
for (let i = startIndex; i < len - 1; i++) {
171+
if (str[i] === '\\' && str[i + 1] === '0') {
172+
count++
173+
i++
174+
}
175+
}
176+
return count
177+
}
178+
179+
export function removeQuery(pathe: string): string {
180+
const queryIndex = pathe.indexOf('?')
181+
if (queryIndex !== -1) {
182+
return path.posix.normalize(pathe.slice(0, queryIndex))
183+
}
184+
return path.posix.normalize(pathe)
185+
}
186+
187+
export function isStartsWithSlash(str: string): boolean {
188+
return str?.startsWith('/')
189+
}
190+
191+
export function appendQuery(id: string, query: [string, string][]): string {
192+
if (!query.length) {
193+
return id
194+
}
195+
196+
return `${id}?${stringifyQuery(query)}`
197+
}
198+
199+
export function stringifyQuery(query: [string, string][]): string {
200+
if (!query.length) {
201+
return ''
202+
}
203+
204+
let queryStr = ''
205+
206+
for (const [key, value] of query) {
207+
queryStr += `${key}${value ? `=${value}` : ''}&`
208+
}
209+
210+
return `${queryStr.slice(0, -1)}`
211+
}
212+
89213
export interface JsPluginExtended extends JsPlugin {
90214
[key: string]: any
91215
}

0 commit comments

Comments
 (0)
Please sign in to comment.