Skip to content

Commit

Permalink
fix: handle local and module scripts separately (vitejs#5464)
Browse files Browse the repository at this point in the history
  • Loading branch information
benmccann authored Nov 9, 2021
1 parent 0a3e514 commit 0713446
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 6 deletions.
54 changes: 48 additions & 6 deletions packages/vite/src/node/optimizer/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs'
import path from 'path'
import glob from 'fast-glob'
import { ResolvedConfig } from '..'
import { Loader, Plugin, build, transform } from 'esbuild'
import { Loader, Plugin, build, transform, OnLoadResult } from 'esbuild'
import {
KNOWN_ASSET_TYPES,
JS_TYPES_RE,
Expand All @@ -18,7 +18,8 @@ import {
externalRE,
dataUrlRE,
multilineCommentsRE,
singlelineCommentsRE
singlelineCommentsRE,
virtualModuleRE
} from '../utils'
import {
createPluginContainer,
Expand All @@ -34,6 +35,8 @@ const debug = createDebugger('vite:deps')

const htmlTypesRE = /\.(html|vue|svelte|astro)$/

const setupRE = /<script\s+setup/

// A simple regex to detect import sources. This is only used on
// <script lang="ts"> blocks in vue (setup only) or svelte files, since
// seemingly unused imports are dropped by esbuild when transpiling TS which
Expand Down Expand Up @@ -146,6 +149,7 @@ export const commentRE = /<!--(.|[\r\n])*?-->/
const srcRE = /\bsrc\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im
const typeRE = /\btype\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im
const langRE = /\blang\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im
const contextRE = /\bcontext\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s'">]+))/im

function esbuildScanPlugin(
config: ResolvedConfig,
Expand Down Expand Up @@ -185,6 +189,8 @@ function esbuildScanPlugin(
return {
name: 'vite:dep-scan',
setup(build) {
const moduleScripts: Record<string, OnLoadResult> = {}

// external urls
build.onResolve({ filter: externalRE }, ({ path }) => ({
path,
Expand All @@ -197,6 +203,16 @@ function esbuildScanPlugin(
external: true
}))

build.onResolve(
{ filter: virtualModuleRE },
async ({ path, importer }) => {
return {
path,
namespace: 'html'
}
}
)

// html types: extract script contents -----------------------------------
build.onResolve({ filter: htmlTypesRE }, async ({ path, importer }) => {
return {
Expand All @@ -205,6 +221,13 @@ function esbuildScanPlugin(
}
})

build.onLoad(
{ filter: virtualModuleRE, namespace: 'html' },
async ({ path }) => {
return moduleScripts[path]
}
)

// extract scripts inside HTML-like files and treat it as a js module
build.onLoad(
{ filter: htmlTypesRE, namespace: 'html' },
Expand All @@ -220,11 +243,10 @@ function esbuildScanPlugin(
let match: RegExpExecArray | null
while ((match = regex.exec(raw))) {
const [, openTag, content] = match
const srcMatch = openTag.match(srcRE)
const typeMatch = openTag.match(typeRE)
const langMatch = openTag.match(langRE)
const type =
typeMatch && (typeMatch[1] || typeMatch[2] || typeMatch[3])
const langMatch = openTag.match(langRE)
const lang =
langMatch && (langMatch[1] || langMatch[2] || langMatch[3])
// skip type="application/ld+json" and other non-JS types
Expand All @@ -241,11 +263,31 @@ function esbuildScanPlugin(
if (lang === 'ts' || lang === 'tsx' || lang === 'jsx') {
loader = lang
}
const srcMatch = openTag.match(srcRE)
if (srcMatch) {
const src = srcMatch[1] || srcMatch[2] || srcMatch[3]
js += `import ${JSON.stringify(src)}\n`
} else if (content.trim()) {
js += content + '\n'
// There can be module scripts (`<script context="module">` in Svelte and `<script>` in Vue)
// or local scripts (`<script>` in Svelte and `<script setup>` in Vue)
// We need to handle these separately in case variable names are reused between them
const contextMatch = openTag.match(contextRE)
const context =
contextMatch &&
(contextMatch[1] || contextMatch[2] || contextMatch[3])
if (
(path.endsWith('.vue') && setupRE.test(raw)) ||
(path.endsWith('.svelte') && context !== 'module')
) {
const id = `virtual-module:${path}`
moduleScripts[id] = {
loader,
contents: content
}
js += `import '${id}';\n`
} else {
js += content + '\n'
}
}
}
// empty singleline & multiline comments to avoid matching comments
Expand All @@ -256,7 +298,7 @@ function esbuildScanPlugin(
if (
loader.startsWith('ts') &&
(path.endsWith('.svelte') ||
(path.endsWith('.vue') && /<script\s+setup/.test(raw)))
(path.endsWith('.vue') && setupRE.test(raw)))
) {
// when using TS + (Vue + <script setup>) or Svelte, imports may seem
// unused to esbuild and dropped in the build output, which prevents
Expand Down
2 changes: 2 additions & 0 deletions packages/vite/src/node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ export const isExternalUrl = (url: string): boolean => externalRE.test(url)
export const dataUrlRE = /^\s*data:/i
export const isDataUrl = (url: string): boolean => dataUrlRE.test(url)

export const virtualModuleRE = /virtual-module:.*/

const knownJsSrcRE = /\.((j|t)sx?|mjs|vue|marko|svelte|astro)($|\?)/
export const isJSRequest = (url: string): boolean => {
url = cleanUrl(url)
Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0713446

Please sign in to comment.