Skip to content

Commit d9627ee

Browse files
authored
feat: make oxc runtime helpers name mangle-able (#306)
1 parent 0d36bef commit d9627ee

File tree

2 files changed

+70
-46
lines changed

2 files changed

+70
-46
lines changed

packages/vite/src/node/__tests__/plugins/oxc.spec.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,18 @@ describe('renderChunk', () => {
177177
'iife',
178178
)
179179
expect(result).toMatchInlineSnapshot(`
180-
"(function() {
181-
"use strict";var babelHelpers=function(exports){function t(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function n(e){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=e.apply(n,r);function s(e){t(o,i,a,s,c,\`next\`,e)}function c(e){t(o,i,a,s,c,\`throw\`,e)}s(void 0)})}}return exports.asyncToGenerator=n,exports}({});
182-
183-
//#region src/index.js
184-
babelHelpers.asyncToGenerator(function* () {
185-
yield new Promise((resolve) => setTimeout(resolve, 1e3));
186-
console.log("foo");
187-
})();
188-
//#endregion
189-
})();
190-
"
191-
`)
180+
"(function() {
181+
"use strict";var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}};babelHelpers_asyncToGenerator=t;})();
182+
183+
//#region src/index.js
184+
babelHelpers_asyncToGenerator(function* () {
185+
yield new Promise((resolve) => setTimeout(resolve, 1e3));
186+
console.log("foo");
187+
})();
188+
//#endregion
189+
})();
190+
"
191+
`)
192192
})
193193

194194
test('should inject helper for iife without exports from cjs', async () => {
@@ -208,17 +208,17 @@ describe('renderChunk', () => {
208208
'iife',
209209
)
210210
expect(result).toMatchInlineSnapshot(`
211-
"(function() {var babelHelpers=function(exports){function t(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function n(e){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=e.apply(n,r);function s(e){t(o,i,a,s,c,\`next\`,e)}function c(e){t(o,i,a,s,c,\`throw\`,e)}s(void 0)})}}return exports.asyncToGenerator=n,exports}({});
212-
213-
//#region src/index.js
214-
babelHelpers.asyncToGenerator(function* () {
215-
yield new Promise((resolve) => setTimeout(resolve, 1e3));
216-
console.log("foo");
217-
})();
218-
//#endregion
219-
})();
220-
"
221-
`)
211+
"(function() {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}};babelHelpers_asyncToGenerator=t;})();
212+
213+
//#region src/index.js
214+
babelHelpers_asyncToGenerator(function* () {
215+
yield new Promise((resolve) => setTimeout(resolve, 1e3));
216+
console.log("foo");
217+
})();
218+
//#endregion
219+
})();
220+
"
221+
`)
222222
})
223223

224224
test('should inject helper for iife with exports', async () => {
@@ -241,10 +241,10 @@ return exports;
241241
'iife',
242242
)
243243
expect(result).toMatchInlineSnapshot(`
244-
"var lib = function(exports) {var babelHelpers=function(exports){function t(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function n(e){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=e.apply(n,r);function s(e){t(o,i,a,s,c,\`next\`,e)}function c(e){t(o,i,a,s,c,\`throw\`,e)}s(void 0)})}}return exports.asyncToGenerator=n,exports}({});
244+
"var lib = function(exports) {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}};babelHelpers_asyncToGenerator=t;})();
245245
246246
//#region entry.js
247-
babelHelpers.asyncToGenerator(function* () {
247+
babelHelpers_asyncToGenerator(function* () {
248248
yield new Promise((resolve) => setTimeout(resolve, 1e3));
249249
console.log("foo");
250250
})();
@@ -279,10 +279,10 @@ return exports;
279279
expect(result).toMatchInlineSnapshot(`
280280
"(function(factory) {
281281
typeof define === "function" && define.amd ? define([], factory) : factory();
282-
})(function() {var babelHelpers=function(exports){function t(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function n(e){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=e.apply(n,r);function s(e){t(o,i,a,s,c,\`next\`,e)}function c(e){t(o,i,a,s,c,\`throw\`,e)}s(void 0)})}}return exports.asyncToGenerator=n,exports}({});
282+
})(function() {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}};babelHelpers_asyncToGenerator=t;})();
283283
284284
//#region entry.js
285-
babelHelpers.asyncToGenerator(function* () {
285+
babelHelpers_asyncToGenerator(function* () {
286286
yield new Promise((resolve) => setTimeout(resolve, 1e3));
287287
console.log("foo");
288288
})();
@@ -316,10 +316,10 @@ exports.foo = foo;
316316
expect(result).toMatchInlineSnapshot(`
317317
"(function(global, factory) {
318318
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.lib = {}));
319-
})(this, function(exports) {var babelHelpers=function(exports){function t(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function n(e){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=e.apply(n,r);function s(e){t(o,i,a,s,c,\`next\`,e)}function c(e){t(o,i,a,s,c,\`throw\`,e)}s(void 0)})}}return exports.asyncToGenerator=n,exports}({});
319+
})(this, function(exports) {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}};babelHelpers_asyncToGenerator=t;})();
320320
321321
//#region entry.js
322-
babelHelpers.asyncToGenerator(function* () {
322+
babelHelpers_asyncToGenerator(function* () {
323323
yield new Promise((resolve) => setTimeout(resolve, 1e3));
324324
console.log("foo");
325325
})();
@@ -355,10 +355,10 @@ return index_default;
355355
expect(result).toMatchInlineSnapshot(`
356356
"(function(global, factory) {
357357
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define([], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.lib = factory());
358-
})(this, function() {var babelHelpers=function(exports){function t(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function n(e){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=e.apply(n,r);function s(e){t(o,i,a,s,c,\`next\`,e)}function c(e){t(o,i,a,s,c,\`throw\`,e)}s(void 0)})}}return exports.asyncToGenerator=n,exports}({});
358+
})(this, function() {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}};babelHelpers_asyncToGenerator=t;})();
359359
360360
//#region entry.js
361-
babelHelpers.asyncToGenerator(function* () {
361+
babelHelpers_asyncToGenerator(function* () {
362362
yield new Promise((resolve) => setTimeout(resolve, 1e3));
363363
console.log("foo");
364364
})();

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

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -431,40 +431,46 @@ export const buildOxcPlugin = (): Plugin => {
431431

432432
const runtimeHelpers = Object.entries(res.helpersUsed)
433433
if (runtimeHelpers.length > 0) {
434+
// The length is kept to avoid sourcemap generation
435+
let newCode = res.code.replace(
436+
/babelHelpers\.([A-Za-z_$][\w$]*)\b/g,
437+
'babelHelpers_$1',
438+
)
439+
434440
const helpersCode = await generateRuntimeHelpers(runtimeHelpers)
435441
switch (opts.format) {
436442
case 'es': {
437-
if (res.code.startsWith('#!')) {
438-
let secondLinePos = res.code.indexOf('\n')
443+
if (newCode.startsWith('#!')) {
444+
let secondLinePos = newCode.indexOf('\n')
439445
if (secondLinePos === -1) {
440446
secondLinePos = 0
441447
}
442448
// inject after hashbang
443-
res.code =
444-
res.code.slice(0, secondLinePos) +
449+
newCode =
450+
newCode.slice(0, secondLinePos) +
445451
helpersCode +
446-
res.code.slice(secondLinePos)
452+
newCode.slice(secondLinePos)
447453
if (res.map) {
448454
res.map.mappings = res.map.mappings.replace(';', ';;')
449455
}
450456
} else {
451-
res.code = helpersCode + res.code
457+
newCode = helpersCode + newCode
452458
if (res.map) {
453459
res.map.mappings = ';' + res.map.mappings
454460
}
455461
}
456462
break
457463
}
458464
case 'cjs': {
459-
if (/^\s*['"]use strict['"];/.test(res.code)) {
465+
if (/^\s*['"]use strict['"];/.test(newCode)) {
460466
// inject after use strict
461-
res.code = res.code.replace(
467+
newCode = newCode.replace(
462468
/^\s*['"]use strict['"];/,
463469
(m) => m + helpersCode,
464470
)
465471
// no need to update sourcemap because the runtime helpers are injected in the same line with "use strict"
466472
} else {
467-
res.code = helpersCode + res.code
473+
newCode = helpersCode + newCode
468474
if (res.map) {
469475
res.map.mappings = ';' + res.map.mappings
470476
}
@@ -486,20 +492,21 @@ export const buildOxcPlugin = (): Plugin => {
486492
case 'umd': {
487493
const m = (
488494
opts.format === 'iife' ? IIFE_BEGIN_RE : UMD_BEGIN_RE
489-
).exec(res.code)
495+
).exec(newCode)
490496
if (!m) {
491497
this.error(`Unexpected ${opts.format.toUpperCase()} format`)
492498
return
493499
}
494500
const pos = m.index + m[0].length
495-
res.code =
496-
res.code.slice(0, pos) + helpersCode + '\n' + res.code.slice(pos)
501+
newCode =
502+
newCode.slice(0, pos) + helpersCode + '\n' + newCode.slice(pos)
497503
break
498504
}
499505
default: {
500506
opts.format satisfies never
501507
}
502508
}
509+
res.code = newCode
503510
}
504511

505512
return res
@@ -529,6 +536,9 @@ export function resolveOxcTranspileOptions(
529536
async function generateRuntimeHelpers(
530537
runtimeHelpers: readonly [string, string][],
531538
): Promise<string> {
539+
const isAsciiOnlyIdentifierRE = /^[A-Za-z_$][\w$]*$/
540+
const cjsExportRE = /\bexports\.([A-Za-z_$][\w$]*)\s*=/g
541+
532542
const bundle = await rolldown({
533543
cwd: url.fileURLToPath(/** #__KEEP__ */ import.meta.url),
534544
input: 'entrypoint',
@@ -553,14 +563,28 @@ async function generateRuntimeHelpers(
553563
},
554564
},
555565
},
566+
{
567+
name: 'ensure-helper-names',
568+
renderChunk(_code, chunk) {
569+
if (chunk.exports.some((e) => !isAsciiOnlyIdentifierRE.test(e))) {
570+
throw new Error(
571+
`Expected all runtime helper export names to be ASCII-only. Got ${chunk.exports.filter((e) => !isAsciiOnlyIdentifierRE.test(e)).join(', ')}`,
572+
)
573+
}
574+
},
575+
},
556576
],
557577
})
558578
const output = await bundle.generate({
559-
format: 'iife',
560-
name: 'babelHelpers',
579+
format: 'cjs',
561580
minify: true,
562581
})
563-
return output.output[0].code
582+
const outputCode = output.output[0].code
583+
const exportNames = [...outputCode.matchAll(cjsExportRE)].map((m) => m[1])
584+
return (
585+
`var ${exportNames.map((n) => `babelHelpers_${n}`).join(', ')};` +
586+
`!(() => {${output.output[0].code.replace(cjsExportRE, ';babelHelpers_$1=')}})();`
587+
)
564588
}
565589

566590
type OxcJsxOptions = Exclude<OxcOptions['jsx'], string | undefined>

0 commit comments

Comments
 (0)