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

chore!: drop ast check for refresh boundary #43

Merged
merged 1 commit into from Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 8 additions & 50 deletions packages/plugin-react/src/fast-refresh.ts
@@ -1,7 +1,6 @@
import fs from 'node:fs'
import path from 'node:path'
import { createRequire } from 'node:module'
import type { types as t } from '@babel/core'

export const runtimePublicPath = '/@react-refresh'

Expand Down Expand Up @@ -67,14 +66,6 @@ const timeout = `
}
`

const footer = `
if (import.meta.hot) {
window.$RefreshReg$ = prevRefreshReg;
window.$RefreshSig$ = prevRefreshSig;

__ACCEPT__
}`

const checkAndAccept = `
function isReactRefreshBoundary(mod) {
if (mod == null || typeof mod !== 'object') {
Expand Down Expand Up @@ -109,47 +100,14 @@ import.meta.hot.accept(mod => {
});
`

export function addRefreshWrapper(
code: string,
id: string,
accept: boolean,
): string {
return (
header.replace('__SOURCE__', JSON.stringify(id)) +
code +
footer.replace('__ACCEPT__', accept ? checkAndAccept : timeout)
)
}

export function isRefreshBoundary(ast: t.File): boolean {
// Every export must be a potential React component.
// We'll also perform a runtime check that's more robust as well (isLikelyComponentType).
return ast.program.body.every((node) => {
if (node.type !== 'ExportNamedDeclaration') {
return true
}
const { declaration, specifiers } = node
if (declaration) {
if (declaration.type === 'ClassDeclaration') return false
if (declaration.type === 'VariableDeclaration') {
return declaration.declarations.every((variable) =>
isComponentLikeIdentifier(variable.id),
)
}
if (declaration.type === 'FunctionDeclaration') {
return !!declaration.id && isComponentLikeIdentifier(declaration.id)
}
}
return specifiers.every((spec) => {
return isComponentLikeIdentifier(spec.exported)
})
})
}
const footer = `
if (import.meta.hot) {
window.$RefreshReg$ = prevRefreshReg;
window.$RefreshSig$ = prevRefreshSig;

function isComponentLikeIdentifier(node: t.Node): boolean {
return node.type === 'Identifier' && isComponentLikeName(node.name)
}
${checkAndAccept}
}`

function isComponentLikeName(name: string): boolean {
return typeof name === 'string' && name[0] >= 'A' && name[0] <= 'Z'
export function addRefreshWrapper(code: string, id: string): string {
return header.replace('__SOURCE__', JSON.stringify(id)) + code + footer
}
15 changes: 3 additions & 12 deletions packages/plugin-react/src/index.ts
@@ -1,13 +1,12 @@
import path from 'node:path'
import type { ParserOptions, TransformOptions, types as t } from '@babel/core'
import type { ParserOptions, TransformOptions } from '@babel/core'
import * as babel from '@babel/core'
import { createFilter, loadEnv, normalizePath, resolveEnvPrefix } from 'vite'
import type { Plugin, PluginOption, ResolvedConfig } from 'vite'
import MagicString from 'magic-string'
import type { SourceMap } from 'magic-string'
import {
addRefreshWrapper,
isRefreshBoundary,
preambleCode,
runtimeCode,
runtimePublicPath,
Expand Down Expand Up @@ -245,7 +244,6 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
}
}

let ast: t.File | null | undefined
let prependReactImport = false
if (!isProjectFile || isJSX) {
if (!useAutomaticRuntime && isProjectFile) {
Expand Down Expand Up @@ -314,14 +312,8 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
parserPlugins.push('typescript')
}

const transformAsync = ast
? babel.transformFromAstAsync.bind(babel, ast, code)
: babel.transformAsync.bind(babel, code)

const isReasonReact = extension.endsWith('.bs.js')
const result = await transformAsync({
const result = await babel.transformAsync(code, {
...babelOptions,
ast: !isReasonReact,
root: projectRoot,
filename: id,
sourceFileName: filepath,
Expand All @@ -344,8 +336,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
if (result) {
let code = result.code!
if (useFastRefresh && /\$RefreshReg\$\(/.test(code)) {
const accept = isReasonReact || isRefreshBoundary(result.ast!)
code = addRefreshWrapper(code, id, accept)
code = addRefreshWrapper(code, id)
}
return {
code,
Expand Down