From 8f04fe649a192adfc7ace8176e5e342f332bfbed Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Tue, 25 Jan 2022 21:08:52 +0800 Subject: [PATCH] wip(app): nvue styler --- packages/uni-app-vite/src/nvue/index.ts | 2 + .../uni-app-vite/src/nvue/plugins/appCss.ts | 150 ++++++++++++++++++ .../uni-app-vite/src/nvue/plugins/esbuild.ts | 15 +- .../uni-app-vite/src/nvue/plugins/mainJs.ts | 3 +- .../uni-app-vue/dist/nvue.runtime.esm.dev.js | 50 +++--- .../uni-app-vue/dist/nvue.runtime.esm.prod.js | 50 +++--- packages/uni-app-vue/lib/nvue.runtime.esm.js | 44 ++--- 7 files changed, 253 insertions(+), 61 deletions(-) create mode 100644 packages/uni-app-vite/src/nvue/plugins/appCss.ts diff --git a/packages/uni-app-vite/src/nvue/index.ts b/packages/uni-app-vite/src/nvue/index.ts index 9e550ccc0..4d5504488 100644 --- a/packages/uni-app-vite/src/nvue/index.ts +++ b/packages/uni-app-vite/src/nvue/index.ts @@ -9,6 +9,7 @@ import { uniEasycomPlugin } from '../plugins/easycom' import { uniManifestJsonPlugin } from '../plugins/manifestJson' import { uniStatsPlugin } from '../plugins/stats' import { uniAppNVuePlugin } from './plugin' +import { uniAppCssPlugin } from './plugins/appCss' import { uniEsbuildPlugin } from './plugins/esbuild' import { uniMainJsPlugin } from './plugins/mainJs' import { uniPagesJsonPlugin } from './plugins/pagesJson' @@ -17,6 +18,7 @@ export { initNVueNodeTransforms } from './plugin' export function initNVuePlugins() { return [ + uniAppCssPlugin(), uniEasycomPlugin({ exclude: UNI_EASYCOM_EXCLUDE }), uniHBuilderXConsolePlugin(), uniMainJsPlugin(), diff --git a/packages/uni-app-vite/src/nvue/plugins/appCss.ts b/packages/uni-app-vite/src/nvue/plugins/appCss.ts new file mode 100644 index 000000000..607e20f01 --- /dev/null +++ b/packages/uni-app-vite/src/nvue/plugins/appCss.ts @@ -0,0 +1,150 @@ +import type { Plugin } from 'vite' +import type { PluginContext, RollupError } from 'rollup' +import path from 'path' +import fs from 'fs-extra' +import qs from 'querystring' +import { + CompilerError, + parse, + SFCBlock, + SFCDescriptor, +} from '@vue/compiler-sfc' +import { hash, preNVueHtml, preNVueJs } from '@dcloudio/uni-cli-shared' + +declare module '@vue/compiler-sfc' { + interface SFCDescriptor { + id: string + } +} + +export const APP_CSS_JS = './app.css.js' +export function uniAppCssPlugin(): Plugin { + const inputDir = process.env.UNI_INPUT_DIR + return { + name: 'uni:app-nvue-app-style', + resolveId(id) { + if (id === APP_CSS_JS) { + return APP_CSS_JS + } + }, + load(id) { + if (id === APP_CSS_JS) { + return genAppStylesCode(inputDir, this) + } + }, + } +} + +const defaultAppStylesCode = `export const AppStyles = []` + +async function genAppStylesCode( + inputDir: string, + pluginContext: PluginContext +) { + const filename = path.resolve(inputDir, 'App.vue') + const descriptor = createAppDescriptor(filename, pluginContext) + if (!descriptor.styles.length) { + return defaultAppStylesCode + } + let stylesCode = `` + const styleVars: string[] = [] + for (let i = 0; i < descriptor.styles.length; i++) { + const style = descriptor.styles[i] + const src = style.src || descriptor.filename + const attrsQuery = attrsToQuery(style.attrs, 'css') + const srcQuery = style.src ? `&src=${descriptor.id}` : `` + const query = `?vue&type=style&index=${i}${srcQuery}&inline` + const styleRequest = src + query + attrsQuery + stylesCode += `\nimport _style_${i} from ${JSON.stringify(styleRequest)}` + styleVars.push(`_style_${i}`) + } + return `${stylesCode} +export const AppStyles = [${styleVars.join(',')}] +` +} + +function readAppCode(filename: string) { + if (!fs.existsSync(filename)) { + return `` + } + const source = fs.readFileSync(filename, 'utf8') + if (source.includes('#endif')) { + return preNVueJs(preNVueHtml(source)) + } + return source +} + +let appDescriptor: SFCDescriptor +function createAppDescriptor( + filename: string, + pluginContext: PluginContext +): SFCDescriptor { + const source = readAppCode(filename) + const id = hash(source) + if (!appDescriptor || appDescriptor.id !== id) { + const { descriptor, errors } = parse(source, { + filename, + }) + descriptor.id = id + if (errors.length) { + errors.forEach((error) => + pluginContext.error(createRollupError(filename, error)) + ) + } + appDescriptor = descriptor + } + return appDescriptor +} + +export function createRollupError( + id: string, + error: CompilerError | SyntaxError +): RollupError { + const { message, name, stack } = error + const rollupError: RollupError = { + id, + plugin: 'vue', + message, + name, + stack, + } + + if ('code' in error && error.loc) { + rollupError.loc = { + file: id, + line: error.loc.start.line, + column: error.loc.start.column, + } + } + + return rollupError +} + +// these are built-in query parameters so should be ignored +// if the user happen to add them as attrs +const ignoreList = ['id', 'index', 'src', 'type', 'lang', 'module'] + +function attrsToQuery( + attrs: SFCBlock['attrs'], + langFallback?: string, + forceLangFallback = false +): string { + let query = `` + for (const name in attrs) { + const value = attrs[name] + if (!ignoreList.includes(name)) { + query += `&${qs.escape(name)}${ + value ? `=${qs.escape(String(value))}` : `` + }` + } + } + if (langFallback || attrs.lang) { + query += + `lang` in attrs + ? forceLangFallback + ? `&lang.${langFallback}` + : `&lang.${attrs.lang}` + : `&lang.${langFallback}` + } + return query +} diff --git a/packages/uni-app-vite/src/nvue/plugins/esbuild.ts b/packages/uni-app-vite/src/nvue/plugins/esbuild.ts index 76c32a0b7..de687697e 100644 --- a/packages/uni-app-vite/src/nvue/plugins/esbuild.ts +++ b/packages/uni-app-vite/src/nvue/plugins/esbuild.ts @@ -1,12 +1,16 @@ +import type { Plugin } from 'vite' +import type { BuildOptions, PluginBuild } from 'esbuild' + import path from 'path' import fs from 'fs-extra' import debug from 'debug' + import { transformWithEsbuild } from '@dcloudio/uni-cli-shared' -import type { BuildOptions, PluginBuild } from 'esbuild' -import type { Plugin } from 'vite' + import { nvueOutDir } from '../../utils' const debugEsbuild = debug('uni:app-nvue-esbuild') + export function uniEsbuildPlugin(): Plugin { let buildOptions: BuildOptions const outputDir = process.env.UNI_OUTPUT_DIR @@ -50,8 +54,11 @@ export function uniEsbuildPlugin(): Plugin { function buildNVuePage(filename: string, options: BuildOptions) { return transformWithEsbuild( - `import NVuePageComponent from './${filename}' -Vue.createApp(NVuePageComponent).mount('#root')`, + `import App from './${filename}' +import { AppStyles } from './app.css.js' +const app = Vue.createApp(App) +app.provide('__appStyles', Vue.useCssStyles(AppStyles)) +app.mount('#root')`, path.join(nvueOutDir(), 'main.js'), options ).then((res) => { diff --git a/packages/uni-app-vite/src/nvue/plugins/mainJs.ts b/packages/uni-app-vite/src/nvue/plugins/mainJs.ts index ff75540fa..89ad2deb3 100644 --- a/packages/uni-app-vite/src/nvue/plugins/mainJs.ts +++ b/packages/uni-app-vite/src/nvue/plugins/mainJs.ts @@ -1,4 +1,5 @@ import { defineUniMainJsPlugin } from '@dcloudio/uni-cli-shared' +import { APP_CSS_JS } from './appCss' export function uniMainJsPlugin() { return defineUniMainJsPlugin((opts) => { @@ -17,7 +18,7 @@ export function uniMainJsPlugin() { } } return { - code: `import './pages.json.js'`, + code: `import './pages.json.js';import('${APP_CSS_JS}').then(()=>{})`, map: { mappings: '' }, } } diff --git a/packages/uni-app-vue/dist/nvue.runtime.esm.dev.js b/packages/uni-app-vue/dist/nvue.runtime.esm.dev.js index 5e515e9ce..39492e1f8 100644 --- a/packages/uni-app-vue/dist/nvue.runtime.esm.dev.js +++ b/packages/uni-app-vue/dist/nvue.runtime.esm.dev.js @@ -10982,39 +10982,50 @@ export function nvueFactory(exports, document) { nextSibling: node => node.nextSibling }; - function isUndef(val) { - return val === undefined || val === null; + function useCssStyles(styles) { + var normalized = {}; + + if (isArray(styles)) { + styles.forEach(style => { + Object.keys(style).forEach(name => { + if (hasOwn(normalized, name)) { + extend(normalized[name], style[name]); + } else { + normalized[name] = style[name]; + } + }); + }); + } + + return normalized; } function parseStylesheet(_ref23) { var { - type + type, + vnode: { + appContext + } } = _ref23; if (!type.__styles) { - var { - styles - } = type; - var normalizedStyles = {}; - - if (isArray(styles)) { - styles.forEach(style => { - Object.keys(style).forEach(name => { - if (hasOwn(normalizedStyles, name)) { - extend(normalizedStyles[name], style[name]); - } else { - normalizedStyles[name] = style[name]; - } - }); - }); + var styles = []; + + if (appContext) { + styles.push(appContext.provides.__appStyles); } - type.__styles = normalizedStyles; + type.styles.forEach(style => styles.push(style)); + type.__styles = useCssStyles(styles); } return type.__styles; } + function isUndef(val) { + return val === undefined || val === null; + } + function patchAttr(el, key, value) { var instance = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; @@ -11527,6 +11538,7 @@ export function nvueFactory(exports, document) { unref: unref, useAttrs: useAttrs, useCssModule: useCssModule, + useCssStyles: useCssStyles, useCssVars: useCssVars, useSSRContext: useSSRContext, useSlots: useSlots, diff --git a/packages/uni-app-vue/dist/nvue.runtime.esm.prod.js b/packages/uni-app-vue/dist/nvue.runtime.esm.prod.js index f535aef0c..bed9501e2 100644 --- a/packages/uni-app-vue/dist/nvue.runtime.esm.prod.js +++ b/packages/uni-app-vue/dist/nvue.runtime.esm.prod.js @@ -9157,39 +9157,50 @@ export function nvueFactory(exports, document) { nextSibling: node => node.nextSibling }; - function isUndef(val) { - return val === undefined || val === null; + function useCssStyles(styles) { + var normalized = {}; + + if (isArray(styles)) { + styles.forEach(style => { + Object.keys(style).forEach(name => { + if (hasOwn(normalized, name)) { + extend(normalized[name], style[name]); + } else { + normalized[name] = style[name]; + } + }); + }); + } + + return normalized; } function parseStylesheet(_ref23) { var { - type + type, + vnode: { + appContext + } } = _ref23; if (!type.__styles) { - var { - styles - } = type; - var normalizedStyles = {}; - - if (isArray(styles)) { - styles.forEach(style => { - Object.keys(style).forEach(name => { - if (hasOwn(normalizedStyles, name)) { - extend(normalizedStyles[name], style[name]); - } else { - normalizedStyles[name] = style[name]; - } - }); - }); + var styles = []; + + if (appContext) { + styles.push(appContext.provides.__appStyles); } - type.__styles = normalizedStyles; + type.styles.forEach(style => styles.push(style)); + type.__styles = useCssStyles(styles); } return type.__styles; } + function isUndef(val) { + return val === undefined || val === null; + } + function patchAttr(el, key, value) { var instance = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; @@ -9698,6 +9709,7 @@ export function nvueFactory(exports, document) { unref: unref, useAttrs: useAttrs, useCssModule: useCssModule, + useCssStyles: useCssStyles, useCssVars: useCssVars, useSSRContext: useSSRContext, useSlots: useSlots, diff --git a/packages/uni-app-vue/lib/nvue.runtime.esm.js b/packages/uni-app-vue/lib/nvue.runtime.esm.js index 01834880e..1f724f518 100644 --- a/packages/uni-app-vue/lib/nvue.runtime.esm.js +++ b/packages/uni-app-vue/lib/nvue.runtime.esm.js @@ -8875,30 +8875,38 @@ const nodeOps = { nextSibling: node => node.nextSibling }; -function isUndef(val) { - return val === undefined || val === null; +function useCssStyles(styles) { + const normalized = {}; + if (isArray(styles)) { + styles.forEach(style => { + Object.keys(style).forEach(name => { + if (hasOwn(normalized, name)) { + extend(normalized[name], style[name]); + } + else { + normalized[name] = style[name]; + } + }); + }); + } + return normalized; } -function parseStylesheet({ type }) { +function parseStylesheet({ type, vnode: { appContext } }) { if (!type.__styles) { - const { styles } = type; - const normalizedStyles = {}; - if (isArray(styles)) { - styles.forEach(style => { - Object.keys(style).forEach(name => { - if (hasOwn(normalizedStyles, name)) { - extend(normalizedStyles[name], style[name]); - } - else { - normalizedStyles[name] = style[name]; - } - }); - }); + const styles = []; + if (appContext) { + styles.push(appContext.provides.__appStyles); } - type.__styles = normalizedStyles; + type.styles.forEach(style => styles.push(style)); + type.__styles = useCssStyles(styles); } return type.__styles; } +function isUndef(val) { + return val === undefined || val === null; +} + function patchAttr(el, key, value, instance = null) { if (instance) { value = transformAttr(el, key, value, instance); @@ -9219,4 +9227,4 @@ const createApp = ((...args) => { return app; }); -export { BaseTransition, Comment, EffectScope, Fragment, KeepAlive, ReactiveEffect, Static, Suspense, Teleport, Text, callWithAsyncErrorHandling, callWithErrorHandling, cloneVNode, compatUtils, computed$1 as computed, createApp, createBlock, createCommentVNode, createElementBlock, createBaseVNode as createElementVNode, createHydrationRenderer, createPropsRestProxy, createRenderer, createSlots, createStaticVNode, createTextVNode, createVNode, customRef, defineAsyncComponent, defineComponent, defineEmits, defineExpose, defineProps, devtools, effect, effectScope, getCurrentInstance, getCurrentScope, getTransitionRawChildren, guardReactiveProps, h, handleError, initCustomFormatter, inject, injectHook, isInSSRComponentSetup, isMemoSame, isProxy, isReactive, isReadonly, isRef, isRuntimeOnly, isShallow, isVNode, markRaw, mergeDefaults, mergeProps, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onServerPrefetch, onUnmounted, onUpdated, openBlock, popScopeId, provide, proxyRefs, pushScopeId, queuePostFlushCb, reactive, readonly, ref, registerRuntimeCompiler, render, renderList, renderSlot, resolveComponent, resolveDirective, resolveDynamicComponent, resolveFilter, resolveTransitionHooks, setBlockTracking, setDevtoolsHook, setTransitionHooks, shallowReactive, shallowReadonly, shallowRef, ssrContextKey, stop, toHandlers, toRaw, toRef, toRefs, transformVNodeArgs, triggerRef, unref, useAttrs, useCssModule, useCssVars, useSSRContext, useSlots, useTransitionState, version, warn$1 as warn, watch, watchEffect, watchPostEffect, watchSyncEffect, withAsyncContext, withCtx, withDefaults, withDirectives, withMemo, withScopeId }; +export { BaseTransition, Comment, EffectScope, Fragment, KeepAlive, ReactiveEffect, Static, Suspense, Teleport, Text, callWithAsyncErrorHandling, callWithErrorHandling, cloneVNode, compatUtils, computed$1 as computed, createApp, createBlock, createCommentVNode, createElementBlock, createBaseVNode as createElementVNode, createHydrationRenderer, createPropsRestProxy, createRenderer, createSlots, createStaticVNode, createTextVNode, createVNode, customRef, defineAsyncComponent, defineComponent, defineEmits, defineExpose, defineProps, devtools, effect, effectScope, getCurrentInstance, getCurrentScope, getTransitionRawChildren, guardReactiveProps, h, handleError, initCustomFormatter, inject, injectHook, isInSSRComponentSetup, isMemoSame, isProxy, isReactive, isReadonly, isRef, isRuntimeOnly, isShallow, isVNode, markRaw, mergeDefaults, mergeProps, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onServerPrefetch, onUnmounted, onUpdated, openBlock, popScopeId, provide, proxyRefs, pushScopeId, queuePostFlushCb, reactive, readonly, ref, registerRuntimeCompiler, render, renderList, renderSlot, resolveComponent, resolveDirective, resolveDynamicComponent, resolveFilter, resolveTransitionHooks, setBlockTracking, setDevtoolsHook, setTransitionHooks, shallowReactive, shallowReadonly, shallowRef, ssrContextKey, stop, toHandlers, toRaw, toRef, toRefs, transformVNodeArgs, triggerRef, unref, useAttrs, useCssModule, useCssStyles, useCssVars, useSSRContext, useSlots, useTransitionState, version, warn$1 as warn, watch, watchEffect, watchPostEffect, watchSyncEffect, withAsyncContext, withCtx, withDefaults, withDirectives, withMemo, withScopeId }; -- GitLab