feat: supports node native import (#5149)

This commit is contained in:
keuby 2023-12-19 13:18:26 +08:00 committed by GitHub
parent a61b325650
commit 6483e1d1ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 193 additions and 1 deletions

View File

@ -3,7 +3,7 @@
"version": "2.36.0",
"description": "A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast",
"main": "lib/index.js",
"module": "es/index.js",
"module": "es/index.mjs",
"types": "es/index.d.ts",
"unpkg": "dist/index.js",
"jsdelivr": "dist/index.js",
@ -46,6 +46,14 @@
"web-types.json",
"README.md"
],
"exports": {
".": {
"import": "./es/index.mjs",
"require": "./lib/index.js",
"types": "./es/index.d.ts"
},
"./*": "./*"
},
"web-types": "./web-types.json",
"lint-staged": {
"*.js": [

View File

@ -0,0 +1,180 @@
const fs = require('fs-extra')
const path = require('path')
const glob = require('fast-glob')
const babel = require('@babel/core')
/**
* @param {('es' | 'lib')[]} formats
*/
module.exports.completePath = async (formats) => {
await Promise.all(
formats.map(async (format) => {
const config = formatConfigs[format]
const files = await glob('**/*.js', {
cwd: config.root,
absolute: true,
onlyFiles: true
})
await Promise.all(
files.map(async (filePath) => {
const code = await fs.readFile(filePath, 'utf-8')
await config.parse(code, filePath, path.dirname(filePath))
})
)
})
)
}
const formatConfigs = {
es: {
root: path.join(__dirname, '../../es'),
async parse (code, filePath, currentDir) {
const suffix = '.mjs'
const result = await babel.transformAsync(code, {
root: this.root,
babelrc: false,
filename: filePath,
sourceType: 'module',
plugins: [
{
visitor: {
ImportDeclaration: ({ node }) => {
const source = node.source.value
const parsedSource = parseSource(source, currentDir, suffix)
if (parsedSource) {
node.source.value = parsedSource
}
},
ExportNamedDeclaration: ({ node }) => {
if (node.source) {
const source = node.source.value
const parsedSource = parseSource(source, currentDir, suffix)
if (parsedSource) {
node.source.value = parsedSource
}
}
},
ExportAllDeclaration: ({ node }) => {
const source = node.source.value
const parsedSource = parseSource(source, currentDir, suffix)
if (parsedSource) {
node.source.value = parsedSource
}
}
}
}
]
})
const newFilePath = replaceExtname(filePath, suffix)
await fs.writeFile(newFilePath, result.code || code)
await fs.unlink(filePath)
}
},
lib: {
root: path.join(__dirname, '../../lib'),
async parse (code, filePath, currentDir) {
const suffix = '.js'
const result = await babel.transformAsync(code, {
root: this.root,
babelrc: false,
filename: filePath,
plugins: [
{
visitor: {
CallExpression: ({ node }) => {
if (
node.callee.type === 'Identifier' &&
node.callee.name === 'require'
) {
const firstArg = node.arguments[0]
if (firstArg.type === 'StringLiteral') {
const source = firstArg.value
const parsedSource = parseSource(source, currentDir, suffix)
if (parsedSource) {
firstArg.value = parsedSource
}
}
}
}
}
}
]
})
await fs.writeFile(filePath, result.code || code)
}
}
}
/**
* @param {string} source
* @param {string} currentDir
* @param {string} suffix
* @returns {string | null}
*/
const parseSource = (source, currentDir, suffix) => {
if (source.startsWith('.')) {
const fullPath = joinPath(currentDir, source)
return fs.existsSync(fullPath)
? path.extname(fullPath)
? source
: joinPath(source, 'index' + suffix)
: source + suffix
} else {
const [pkgName, subpath] = splitSource(source) || []
return pkgName == null || subpath == null
? null
: guessFullPath(pkgName, subpath)
}
}
/**
* @param {string} pkgName
* @param {string} subpath
* @return {string | null}
*/
const guessFullPath = (pkgName, subpath) => {
const pkgPath = require.resolve(path.posix.join(pkgName, 'package.json'))
const pkgRootPath = path.dirname(pkgPath)
let parsedSource = null
const sourcePath = path.join(pkgRootPath, subpath)
if (fs.existsSync(sourcePath + '.js')) {
parsedSource = joinPath(pkgName, subpath + '.js')
} else if (fs.existsSync(sourcePath + '.mjs')) {
parsedSource = joinPath(pkgName, subpath + '.mjs')
} else if (fs.existsSync(path.join(sourcePath, 'index.js'))) {
parsedSource = joinPath(pkgName, subpath, 'index.js')
} else if (fs.existsSync(path.join(sourcePath, 'index.mjs'))) {
parsedSource = joinPath(pkgName, subpath, 'index.mjs')
}
return parsedSource
}
const splitSource = (() => {
const splitRegex = /^([\w-]+|@[\w-]+\/[\w-]+)(?:\/(.*))?$/
/**
* @param {string} source
* @return {[string, string] | null}
*/
return (source) => {
const matched = splitRegex.exec(source)
if (!matched) return null
return matched.slice(1)
}
})()
const replaceExtname = (filePath, ext) => {
const oldExt = path.extname(filePath)
if (!oldExt) return filePath + ext
return joinPath(path.dirname(filePath), path.basename(filePath, oldExt) + ext)
}
const joinPath = (firstPath, ...restPath) => {
const joinedPath = normalizePath(path.join(firstPath, ...restPath))
return firstPath.startsWith('./') ? './' + joinedPath : joinedPath
}
/**
* @param {string} path
*/
const normalizePath = (path) => path.replace(/\\/g, '/')

View File

@ -5,6 +5,7 @@ const { terseCssr } = require('./terse-cssr')
const { replaceDefine, outDirs, srcDir } = require('../utils')
const { genWebTypes } = require('./gen-web-types')
const { completePath } = require('./complete-path')
;(async () => {
await terseCssr()
@ -18,6 +19,9 @@ const { genWebTypes } = require('./gen-web-types')
"'date-fns'//": "'date-fns/esm'"
})
// complete require and import source path
await completePath(['es'])
// generate web-types.json for webstorm & vetur
// web-types.json is only a very loose description for auto-complete
// vscode is a much better choice