UNPKG

@94ai/vue2-runtime-helpers

Version:

vue2 runtime helpers for Vue SFC.

519 lines (499 loc) 14.6 kB
## Introduction High level utilities for compiling Vue2 single file components, runtime helpers for @94ai/vite-plugin-vue2 . This package contains high level utilities that you can also use if you are writing a plugin / transform for a bundler or module system that compiles Vue single file components into JavaScript. ## Usage ```typescript // build.ts import path from 'path' import fs from 'fs' import { fileURLToPath } from 'url' import fsExtra from 'fs-extra' import type { LibraryFormats } from 'vite' import { build } from 'vite' import type { ModuleFormat } from 'rollup' import babel from '@rollup/plugin-babel' import vue from '@94ai/vite-plugin-vue2' import vueJsx from '@vitejs/plugin-vue2-jsx' import dts from 'vite-plugin-dts' import scriptSetup from 'unplugin-vue2-script-setup/vite' import Components from 'unplugin-vue-components/vite' import requireTransform from 'vite-plugin-require-transform' import defineOptions from '@94ai/unplugin-vue-define-options/vite' import { NfCommonUIResolver } from '@94ai/common-ui-resolver' // import { assemble, createDefaultCompiler } from '@vue/component-compiler' // @ts-ignore import { output, src } from './config.ts' // @ts-ignore import { copyFile, getComponentDts } from './utils/index.ts' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const entryDir = path.resolve(__dirname, src) const outputDir = path.resolve(__dirname, output) const components: string[] = [] const themes: string[] = [] fs.readdirSync(entryDir).forEach((name) => { const componentDir = path.resolve(entryDir, name) const isDir = fs.lstatSync(componentDir).isDirectory() if (isDir && fs.readdirSync(componentDir).includes('package.json')) { if (name.includes('nf-theme-')) { themes.push(name) } else { components.push(name) } } }) const getAutoImport = (codepenUmd: boolean) => { if (codepenUmd) { return Components({ include: [ /\.vue$/, /\.vue\?vue/, /\.md$/, /\.md\?vue/, /\.jsx$/, /\.jsx\?vue/, /\.tsx$/, /\.tsx\?vue/, ], extensions: ['vue', 'md', 'jsx', 'tsx', 'mjs', 'mts'], resolvers: [ NfCommonUIResolver({ mode: 'packages', theme: [], }), ], }) } return null } // @ts-ignore // eslint-disable-next-line no-unused-vars const getExternal = (codepenUmd: boolean) => { const external = [ 'vue', 'vue-router', 'element-ui', 'vue-demi', '@94ai/vue2-runtime-helpers', // 👈 配置构建工具external ] // if (!codepenUmd) { // return [ // ...external, // ...components.concat(themes).map((_) => `${libPrefix}/${_}`), // ] // } return external } // @ts-ignore // eslint-disable-next-line no-unused-vars const getGlobals = (codepenUmd: boolean) => { const globals = { vue: 'Vue', 'vue-router': 'VueRouter', 'element-ui': 'ELEMENT', 'vue-demi': 'VueDemi', } // if (codepenUmd) { // return { // ...globals, // ...components.reduce((component, key) => { // component[`${libPrefix}/${key}`] = key // return component // }, {}), // } // } return globals } const getFormats = (codepenUmd: boolean): LibraryFormats[] => { if (codepenUmd) { return ['umd', 'es'] } else { return ['es', 'cjs'] } } const handlePackBundle = async (name, codepenUmd = false) => { const entryRoot = path.resolve(__dirname, `${src}/${name}`) await build({ optimizeDeps: { exclude: ['vue-demi'], }, configFile: false, publicDir: false, resolve: { alias: [ { find: /^packages\//, replacement: path.resolve('packages') + '/', }, { find: /^lib\//, replacement: path.resolve('lib') + '/', }, // { // find: /^vue$/, // replacement: 'vue-demi', // }, ], }, // esbuild: { // tsconfigRaw: { // compilerOptions: { // jsxFactory: 'h', // jsxFragmentFactory: 'Fragment', // }, // }, // jsxInject: "import { h } from 'vue-demi';", // }, plugins: [ codepenUmd ? undefined : dts({ entryRoot, include: [ `${entryRoot}/*.ts`, `${entryRoot}/*.tsx`, `${entryRoot}/*.d.ts`, `${entryRoot}/*.vue`, `${entryRoot}/**/*.ts`, `${entryRoot}/**/*.tsx`, `${entryRoot}/**/*.d.ts`, `${entryRoot}/**/*.vue`, ], }), vue({ normalizerCode: codepenUmd // 👈 指定cjs和esm构建把helpers插入的函数转成import ? '' : ` import { normalizeComponent } from '@94ai/vue2-runtime-helpers' export default normalizeComponent`, include: [/\.vue$/, /\.md$/, /\.jsx$/, /\.tsx$/], }), defineOptions(), scriptSetup({ importHelpersFrom: 'vue-demi', include: [/\.vue$/, /\.md$/, /\.jsx$/, /\.tsx$/], }), vueJsx({ compositionAPI: 'vue-demi', include: [/\.jsx$/, /\.tsx$/], }), getAutoImport(codepenUmd), ], build: { sourcemap: false, minify: !!codepenUmd, rollupOptions: { external: getExternal(codepenUmd), output: { exports: 'named', assetFileNames: ({ name: assetsName }): string => { if (/\.(gif|jpe?g|png|svg)$/.test(assetsName ?? '')) { return 'lib/[name]-[hash][extname]' } if (/\.css$/.test(assetsName ?? '')) { return `lib/${name}[extname]` } return assetsName as string }, globals: getGlobals(codepenUmd), }, plugins: [ requireTransform({ fileRegex: /.js$|.vue$|.jsx$|.tsx$.ts$/, }), // { // name: 'customize-assemble', // async transform(code, id) { // // 使用assemble处理SFC文件,并应用优化 // if (id.endsWith('.vue')) { // const compiler = createDefaultCompiler() // const filename = resolve(id) // const descriptor = compiler.compileToDescriptor(filename, code) // const result = await assemble(compiler, filename, descriptor, { // normalizer: '~@94ai/vue2-runtime-helpers', // }) // return { // code: result.code, // map: result.map, // } // } // }, // }, codepenUmd ? babel({ babelHelpers: 'runtime', exclude: 'node_modules/**', extensions: [ '.ts', '.js', '.jsx', '.es6', '.es', '.mjs', '.tsx', '.mtx', '.vue', ], }) : null, ], }, lib: { entry: path.resolve(entryDir, `${name}/lib/index.ts`), name, fileName: (format: ModuleFormat, entryName: string) => { if (entryName.indexOf('style') > -1) { return '' } if (codepenUmd) { return `${name}.${format}.browser.js` } return `lib/${name}.${format === 'es' ? 'esm-bundler' : format}.js` }, formats: getFormats(codepenUmd), }, outDir: path.resolve( outputDir, codepenUmd ? `codepen/lib/${name}` : name ), }, }) } const handleConfigFiles = async (name) => { const destination = outputDir + `/${name}` const resource = entryDir + `/${name}` const handleEslint = () => { const eslintConfig = eval( '(' + fs .readFileSync(path.join(__dirname, `.eslintrc.cjs`), { encoding: 'utf8', }) .replace('module.exports = ', '') + ')' ) eslintConfig.extends.length = 1 const fileStr = JSON.stringify(eslintConfig, null, 2) fsExtra.outputFileSync( path.join(destination, '.eslintrc.cjs'), 'module.exports = ' + fileStr, 'utf-8' ) } const handlePkg = () => { const srcPkg = JSON.parse( fs.readFileSync(path.join(resource, `package.json`), { encoding: 'utf8', }) ) const libPkg: Record<string, any> = {} ;[ 'name', 'version', 'description', 'keywords', 'author', 'homepage', 'license', 'publishConfig', 'repository', ].forEach((key) => { libPkg[key] = srcPkg[key] }) if (name !== 'codepen') { ;['devDependencies', 'dependencies', 'peerDependenciesMeta'].forEach( (key) => { libPkg[key] = srcPkg[key] } ) libPkg['peerDependencies'] = { ...(srcPkg['peerDependencies'] || {}), 'element-ui': '>=2.13.2', } } libPkg.types = `lib/index.d.ts` libPkg.main = `lib/${name}.cjs.js` libPkg.module = `lib/${name}.esm-bundler.js` const fileStr = JSON.stringify(libPkg, null, 2) fsExtra.outputFileSync( path.join(destination, `package.json`), fileStr, 'utf-8' ) } const handlePostcss = () => { fs.copyFileSync( path.join(__dirname, 'postcss.config.mjs'), path.join(destination, 'postcss.config.js'), fs.constants.COPYFILE_FICLONE ) } const handleReadMe = () => { fs.copyFileSync( path.join(resource, `README.md`), path.join(destination, 'README.md'), fs.constants.COPYFILE_FICLONE ) } const handleTheme = (name) => { if (name !== 'codepen') { const dir = path.resolve(__dirname, output, name, 'lib', 'style') if (!fsExtra.existsSync(dir)) { fsExtra.mkdirsSync(dir) } fs.copyFileSync( path.resolve(__dirname, src, name, output, 'style/css.ts'), path.resolve(__dirname, output, name, output, 'style/css.js'), fs.constants.COPYFILE_FICLONE ) fs.copyFileSync( path.resolve(__dirname, src, name, output, 'style/index.ts'), path.resolve(__dirname, output, name, output, 'style/index.js'), fs.constants.COPYFILE_FICLONE ) } } const handleGlobalDts = (name) => { if (name !== 'codepen') { const prefix = `export {} declare module 'vue' { export interface GlobalComponents { ` const after = ` } }` let content = `` if (name === 'common-ui') { for (const name of components) { if (name !== 'common-ui' && name !== 'codepen') { content += getComponentDts(name) } } } else { content = getComponentDts(name) } fsExtra.outputFileSync( path.join(destination, `components.d.ts`), prefix + content + after, 'utf-8' ) } } handlePkg() handleEslint() handlePostcss() handleReadMe() handleTheme(name) handleGlobalDts(name) } const handleThemePack = (name?: string) => { fsExtra.ensureDirSync(path.resolve(__dirname, 'lib')) if (name) { copyFile( path.resolve(__dirname, src, name), path.resolve(__dirname, output, name) ) handleSyncVersion(name) } else { themes.forEach((sigle) => { copyFile( path.resolve(__dirname, src, sigle), path.resolve(__dirname, output, sigle) ) handleSyncVersion(sigle) }) } console.log(`${name ?? themes.join(',')}主题构建完成`) } const handleSyncVersion = async (name) => { const targetPackagePath = path.resolve( __dirname, output, name, `package.json` ) let version if (name !== 'common-ui') { const mainPackagePath = path.resolve( __dirname, output, `common-ui/package.json` ) if (fs.existsSync(mainPackagePath)) { const commonLibPkg = JSON.parse( fs.readFileSync(mainPackagePath, { encoding: 'utf8', }) ) version = commonLibPkg.dependencies[`@94ai/${name}`] } if (!version || version?.indexOf('workspace:^') > -1) { version = JSON.parse( fs.readFileSync(path.resolve(__dirname, 'lerna.json'), { encoding: 'utf8', }) ).version } } else { version = JSON.parse( fs.readFileSync(path.resolve(__dirname, 'lerna.json'), { encoding: 'utf8', }) ).version } const targetLibPkg = JSON.parse( fs.readFileSync(targetPackagePath, { encoding: 'utf8', }) ) targetLibPkg.version = version .replace('^', '') .replace('~', '') .replace('@', '') fsExtra.outputFileSync( targetPackagePath, JSON.stringify(targetLibPkg, null, 2), 'utf-8' ) } const sourcePackageHandler = (name) => { const tagrgetPath = path.resolve(__dirname, output, name, 'package') fsExtra.ensureDirSync(tagrgetPath) copyFile(path.resolve(__dirname, src, name, 'lib'), tagrgetPath) } const buildLib = async () => { const singles = process.argv.slice(2) if (singles?.length) { for (const single of singles) { if (themes.includes(single)) { handleThemePack(single) continue } await handlePackBundle(single) await handleConfigFiles(single) if (single !== 'codepen') { await handlePackBundle(single, true) } await handleSyncVersion(single) sourcePackageHandler(single) } } else { handleThemePack() for (const name of components) { await handlePackBundle(name) await handleConfigFiles(name) await handleSyncVersion(name) sourcePackageHandler(name) } for (const name of components) { if (name !== 'codepen') { await handlePackBundle(name, true) } } } } buildLib() ``` ## License This content is released under the [MIT](http://opensource.org/licenses/MIT) License. 此内容在[MIT]下发布(http://opensource.org/licenses/MIT)许可证。