2021-08-10 00:27:23 +00:00
|
|
|
import { createUnplugin } from 'unplugin'
|
|
|
|
import { parseQuery, parseURL } from 'ufo'
|
|
|
|
import { IdentifierMap } from './types'
|
|
|
|
|
2021-08-30 11:55:57 +00:00
|
|
|
const excludeRE = [
|
2021-08-10 00:27:23 +00:00
|
|
|
// imported from other module
|
2021-08-30 11:55:57 +00:00
|
|
|
/\bimport\s*([\w_$]*?),?\s*\{([\s\S]*?)\}\s*from\b/g,
|
2021-08-10 00:27:23 +00:00
|
|
|
// defined as function
|
|
|
|
/\bfunction\s*([\s\S]+?)\s*\(/g,
|
|
|
|
// defined as local variable
|
2021-10-02 16:59:32 +00:00
|
|
|
/\b(?:const|let|var)\s*(\{([\s\S]*?)\}|[\w\d_$]+?\b)/g
|
2021-08-10 00:27:23 +00:00
|
|
|
]
|
|
|
|
|
2021-08-30 11:55:57 +00:00
|
|
|
const multilineCommentsRE = /\/\*(.|[\r\n])*?\*\//gm
|
2021-10-02 16:59:32 +00:00
|
|
|
const singlelineCommentsRE = /^\s*\/\/.*$/gm
|
2021-08-30 11:55:57 +00:00
|
|
|
|
|
|
|
function stripeComments (code: string) {
|
|
|
|
return code
|
|
|
|
.replace(multilineCommentsRE, '')
|
|
|
|
.replace(singlelineCommentsRE, '')
|
|
|
|
}
|
|
|
|
|
2021-08-24 07:49:03 +00:00
|
|
|
export const TransformPlugin = createUnplugin((map: IdentifierMap) => {
|
2021-08-30 11:55:57 +00:00
|
|
|
const matchRE = new RegExp(`\\b(${Object.keys(map).join('|')})\\b`, 'g')
|
2021-08-10 00:27:23 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
name: 'nuxt-global-imports-transform',
|
|
|
|
enforce: 'post',
|
|
|
|
transformInclude (id) {
|
|
|
|
const { pathname, search } = parseURL(id)
|
|
|
|
const query = parseQuery(search)
|
|
|
|
|
|
|
|
if (id.includes('node_modules')) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// vue files
|
|
|
|
if (pathname.endsWith('.vue') && (query.type === 'template' || !search)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// js files
|
|
|
|
if (pathname.match(/\.((c|m)?j|t)sx?/g)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
transform (code) {
|
2021-08-30 11:55:57 +00:00
|
|
|
// strip comments so we don't match on them
|
|
|
|
const withoutComment = stripeComments(code)
|
|
|
|
|
2021-08-10 00:27:23 +00:00
|
|
|
// find all possible injection
|
2021-08-30 11:55:57 +00:00
|
|
|
const matched = new Set(Array.from(withoutComment.matchAll(matchRE)).map(i => i[1]))
|
2021-08-10 00:27:23 +00:00
|
|
|
|
|
|
|
// remove those already defined
|
2021-08-30 11:55:57 +00:00
|
|
|
for (const regex of excludeRE) {
|
|
|
|
Array.from(withoutComment.matchAll(regex))
|
|
|
|
.flatMap(i => [...(i[1]?.split(',') || []), ...(i[2]?.split(',') || [])])
|
2021-08-10 00:27:23 +00:00
|
|
|
.forEach(i => matched.delete(i.trim()))
|
|
|
|
}
|
|
|
|
|
2021-08-30 11:55:57 +00:00
|
|
|
if (!matched.size) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
2021-08-10 00:27:23 +00:00
|
|
|
const modules: Record<string, string[]> = {}
|
|
|
|
|
|
|
|
// group by module name
|
|
|
|
Array.from(matched).forEach((name) => {
|
|
|
|
const moduleName = map[name]!
|
|
|
|
if (!modules[moduleName]) {
|
|
|
|
modules[moduleName] = []
|
|
|
|
}
|
|
|
|
modules[moduleName].push(name)
|
|
|
|
})
|
|
|
|
|
2021-10-02 16:59:32 +00:00
|
|
|
// Needed for webpack4/bridge support
|
|
|
|
const isCJSContext = code.includes('require(')
|
|
|
|
|
2021-08-10 00:27:23 +00:00
|
|
|
// stringify import
|
2021-10-02 16:59:32 +00:00
|
|
|
const imports = !isCJSContext
|
|
|
|
? Object.entries(modules)
|
|
|
|
.map(([moduleName, names]) => `import { ${names.join(',')} } from '${moduleName}';`)
|
|
|
|
.join('')
|
|
|
|
: Object.entries(modules)
|
|
|
|
.map(([moduleName, names]) => `const { ${names.join(',')} } = require('${moduleName}');`)
|
|
|
|
.join('')
|
2021-08-10 00:27:23 +00:00
|
|
|
|
|
|
|
return imports + code
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|