Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin-legacy): Add experimental option to use swc. #4105

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/playground/legacy-swc/__tests__/legacy-swc.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { isBuild } from '../../testUtils'

test('should work', async () => {
expect(await page.textContent('#app')).toMatch('Hello')
})

test('import.meta.env.LEGACY', async () => {
expect(await page.textContent('#env')).toMatch(isBuild ? 'true' : 'false')
})

// https://github.com/vitejs/vite/issues/3400
test('transpiles down iterators correctly', async () => {
expect(await page.textContent('#iterators')).toMatch('hello')
})

test('wraps with iife', async () => {
expect(await page.textContent('#babel-helpers')).toMatch(
'exposed babel helpers: false'
)
})
5 changes: 5 additions & 0 deletions packages/playground/legacy-swc/async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function fn() {
const m = new Map()
m.set('foo', 'Hello')
document.querySelector('#app').textContent = m.get('foo')
}
5 changes: 5 additions & 0 deletions packages/playground/legacy-swc/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<h1 id="app"></h1>
<div id="env"></div>
<div id="iterators"></div>
<div id="babel-helpers"></div>
<script type="module" src="./main.js"></script>
32 changes: 32 additions & 0 deletions packages/playground/legacy-swc/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
async function run() {
const { fn } = await import('./async.js')
fn()
}

run()

let isLegacy

// make sure that branching works despite esbuild's constant folding (#1999)
if (import.meta.env.LEGACY) {
if (import.meta.env.LEGACY === true) isLegacy = true
} else {
if (import.meta.env.LEGACY === false) isLegacy = false
}

text('#env', `is legacy: ${isLegacy}`)

// Iterators
text('#iterators', [...new Set(['hello'])].join(''))

// babel-helpers
// Using `String.raw` to inject `@babel/plugin-transform-template-literals`
// helpers.
text(
'#babel-helpers',
String.raw`exposed babel helpers: ${window._templateObject != null}`
)

function text(el, text) {
document.querySelector(el).textContent = text
}
14 changes: 14 additions & 0 deletions packages/playground/legacy-swc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "test-legacy-swc",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build --debug legacy",
"debug": "node --inspect-brk ../../vite/bin/vite",
"serve": "vite preview"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^1.0.0"
}
}
30 changes: 30 additions & 0 deletions packages/playground/legacy-swc/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const fs = require('fs')
const path = require('path')
const legacy = require('@vitejs/plugin-legacy').default

module.exports = {
plugins: [
legacy({
targets: 'IE 11',
experimentalUseSWC: true
})
],

build: {
// make tests faster
minify: false
// sourcemap: true // TODO fix this on SWC side.
},

// special test only hook
// for tests, remove `<script type="module">` tags and remove `nomodule`
// attrs so that we run the legacy bundle instead.
__test__() {
const indexPath = path.resolve(__dirname, './dist/index.html')
let index = fs.readFileSync(indexPath, 'utf-8')
index = index
.replace(/<script type="module".*?<\/script>/g, '')
.replace(/<script nomodule/g, '<script')
fs.writeFileSync(indexPath, index)
}
}
144 changes: 144 additions & 0 deletions packages/plugin-legacy/babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const { legacyEnvVarMarker } = require('./constants.js')

// lazy load babel since it's not used during dev
let babel
/**
* @return {import('@babel/standalone')}
*/
const loadBabel = () => babel || (babel = require('@babel/standalone'))

/**
* @param {string} code
* @param {any} targets
* @param {Set<string>} list
*/
module.exports.detectPolyfills = function detectPolyfills(code, targets, list) {
const { ast } = loadBabel().transform(code, {
ast: true,
babelrc: false,
configFile: false,
presets: [
[
'env',
{
targets,
modules: false,
useBuiltIns: 'usage',
corejs: { version: 3, proposals: false },
shippedProposals: true,
ignoreBrowserslistConfig: true
}
]
]
})
for (const node of ast.program.body) {
if (node.type === 'ImportDeclaration') {
const source = node.source.value
if (
source.startsWith('core-js/') ||
source.startsWith('regenerator-runtime/')
) {
list.add(source)
}
}
}
}

/**
* @typedef {object} TransformChunkOptions
* @property {boolean} sourceMaps
* @property {boolean} needPolyfills
* @property {Set<string>} legacyPolyfills
* @property {import('rollup').SourceMap} inputSourceMap
* @property {import('.').Options['targets']} targets
* @property {import('.').Options['ignoreBrowserslistConfig']} ignoreBrowserslistConfig
*/

/**
* @param {string} raw
* @param {TransformChunkOptions} options
* @returns {*}
*/
module.exports.transformChunk = function transformChunk(raw, options) {
return loadBabel().transform(raw, {
babelrc: false,
configFile: false,
compact: true,
sourceMaps: options.sourceMaps,
inputSourceMap: options.inputSourceMap,
presets: [
// forcing our plugin to run before preset-env by wrapping it in a
// preset so we can catch the injected import statements...
[
() => ({
plugins: [
recordAndRemovePolyfillBabelPlugin(options.legacyPolyfills),
replaceLegacyEnvBabelPlugin(),
wrapIIFEBabelPlugin()
]
})
],
[
'env',
{
targets: options.targets,
modules: false,
bugfixes: true,
loose: false,
useBuiltIns: options.needPolyfills ? 'usage' : false,
corejs: options.legacyPolyfills
? { version: 3, proposals: false }
: undefined,
shippedProposals: true,
ignoreBrowserslistConfig: options.ignoreBrowserslistConfig
}
]
]
})
}

/**
* @param {Set<string>} polyfills
*/
function recordAndRemovePolyfillBabelPlugin(polyfills) {
return ({ types: t }) => ({
name: 'vite-remove-polyfill-import',
post({ path }) {
path.get('body').forEach((p) => {
if (t.isImportDeclaration(p)) {
polyfills.add(p.node.source.value)
p.remove()
}
})
}
})
}

function replaceLegacyEnvBabelPlugin() {
return ({ types: t }) => ({
name: 'vite-replace-env-legacy',
visitor: {
Identifier(path) {
if (path.node.name === legacyEnvVarMarker) {
path.replaceWith(t.booleanLiteral(true))
}
}
}
})
}

function wrapIIFEBabelPlugin() {
return ({ types: t, template }) => {
const buildIIFE = template(';(function(){%%body%%})();')

return {
name: 'vite-wrap-iife',
post({ path }) {
if (!this.isWrapped) {
this.isWrapped = true
path.replaceWith(t.program(buildIIFE({ body: path.node.body })))
}
}
}
}
}
1 change: 1 addition & 0 deletions packages/plugin-legacy/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports.legacyEnvVarMarker = `__VITE_IS_LEGACY__`
4 changes: 4 additions & 0 deletions packages/plugin-legacy/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export interface Options {
* default: true
*/
renderLegacyChunks?: boolean
/**
* default: false
*/
experimentalUseSWC?: boolean
}

declare function createPlugin(options?: Options): Plugin
Expand Down
Loading