提交 76e85b8b 编写于 作者: fxy060608's avatar fxy060608

wip(mp): support global registration

上级 ae7ec06d
...@@ -2,7 +2,7 @@ import path from 'path' ...@@ -2,7 +2,7 @@ import path from 'path'
import { ResolvedId } from 'rollup' import { ResolvedId } from 'rollup'
import { transformVueComponentImports } from '../src/mp/transformImports' import { transformVueComponentImports } from '../src/mp/transformImports'
const root = '/usr/xxx/projects/test/src' const root = '/usr/xxx/projects/test/src'
const importer = '/usr/xxx/projects/test/src/pages/index/index.vue'
async function resolve(id: string, importer?: string) { async function resolve(id: string, importer?: string) {
return { return {
id: importer ? path.resolve(path.dirname(importer), id) : id, id: importer ? path.resolve(path.dirname(importer), id) : id,
...@@ -14,6 +14,46 @@ function dynamicImport(name: string, source: string) { ...@@ -14,6 +14,46 @@ function dynamicImport(name: string, source: string) {
} }
describe('transformVueComponentImports', () => { describe('transformVueComponentImports', () => {
describe('global', () => {
const importer = '/usr/xxx/projects/test/src/main.js'
test(`basic`, async () => {
const source = `
import { createSSRApp } from 'vue'
import ComponentA from './components/component-a.vue'
import ComponentB from './components/component-b.vue'
export function createApp() {
const app = createSSRApp(App)
app.component('component-a',ComponentA)
app.component('component-b',ComponentB)
return {
app
}
}
`
const { code, usingComponents } = await transformVueComponentImports(
source,
importer,
{
root,
global: true,
resolve,
dynamicImport,
}
)
expect(code).toContain(
`const ComponentA = ()=>import('${root}/components/component-a.vue')`
)
expect(code).toContain(
`const ComponentB = ()=>import('${root}/components/component-b.vue')`
)
expect(usingComponents).toMatchObject({
'component-a': '/components/component-a',
'component-b': '/components/component-b',
})
})
})
describe('local', () => {
const importer = '/usr/xxx/projects/test/src/pages/index/index.vue'
test(`basic`, async () => { test(`basic`, async () => {
const source = `import test1 from "${root}/components/test1.vue"; const source = `import test1 from "${root}/components/test1.vue";
const _sfc_main = { const _sfc_main = {
...@@ -28,7 +68,7 @@ describe('transformVueComponentImports', () => { ...@@ -28,7 +68,7 @@ describe('transformVueComponentImports', () => {
import "${importer}?vue&type=style&index=0&lang.css"; import "${importer}?vue&type=style&index=0&lang.css";
import _export_sfc from "plugin-vue:export-helper"; import _export_sfc from "plugin-vue:export-helper";
export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]); export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
` `
const { code, usingComponents } = await transformVueComponentImports( const { code, usingComponents } = await transformVueComponentImports(
source, source,
importer, importer,
...@@ -64,7 +104,7 @@ describe('transformVueComponentImports', () => { ...@@ -64,7 +104,7 @@ describe('transformVueComponentImports', () => {
import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css"; import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css";
import _export_sfc from "plugin-vue:export-helper"; import _export_sfc from "plugin-vue:export-helper";
export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]); export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
` `
const { code, usingComponents } = await transformVueComponentImports( const { code, usingComponents } = await transformVueComponentImports(
source, source,
importer, importer,
...@@ -100,7 +140,7 @@ describe('transformVueComponentImports', () => { ...@@ -100,7 +140,7 @@ describe('transformVueComponentImports', () => {
import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css"; import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css";
import _export_sfc from "plugin-vue:export-helper"; import _export_sfc from "plugin-vue:export-helper";
export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]); export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
` `
const { code, usingComponents } = await transformVueComponentImports( const { code, usingComponents } = await transformVueComponentImports(
source, source,
importer, importer,
...@@ -136,7 +176,7 @@ describe('transformVueComponentImports', () => { ...@@ -136,7 +176,7 @@ describe('transformVueComponentImports', () => {
}); });
import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css"; import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css";
export default _sfc_main; export default _sfc_main;
` `
const { code, usingComponents } = await transformVueComponentImports( const { code, usingComponents } = await transformVueComponentImports(
source, source,
importer, importer,
...@@ -172,7 +212,7 @@ describe('transformVueComponentImports', () => { ...@@ -172,7 +212,7 @@ describe('transformVueComponentImports', () => {
}); });
import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css"; import "${root}/pages/index/index.vue?vue&type=style&index=0&lang.css";
export default _sfc_main; export default _sfc_main;
` `
const { code, usingComponents } = await transformVueComponentImports( const { code, usingComponents } = await transformVueComponentImports(
source, source,
importer, importer,
...@@ -191,4 +231,5 @@ describe('transformVueComponentImports', () => { ...@@ -191,4 +231,5 @@ describe('transformVueComponentImports', () => {
'my-component-name': '/components/test1', 'my-component-name': '/components/test1',
}) })
}) })
})
}) })
...@@ -18,4 +18,6 @@ export const M = { ...@@ -18,4 +18,6 @@ export const M = {
'i18n.fallbackLocale.missing': 'i18n.fallbackLocale.missing':
'当前应用配置的 fallbackLocale 或 locale 为:{locale},但 locale 目录缺少该语言文件', '当前应用配置的 fallbackLocale 或 locale 为:{locale},但 locale 目录缺少该语言文件',
'easycom.conflict': 'easycom组件冲突:', 'easycom.conflict': 'easycom组件冲突:',
'mp.component.args[0]': '{0}的第一个参数必须为静态字符串',
'mp.component.args[1]': '{0}需要两个参数',
} }
import { parse, ParserPlugin } from '@babel/parser' import { parse, ParserPlugin } from '@babel/parser'
import { import {
ImportDeclaration, ImportDeclaration,
isCallExpression,
isIdentifier, isIdentifier,
isImportDeclaration, isImportDeclaration,
isMemberExpression,
isObjectExpression, isObjectExpression,
isObjectProperty, isObjectProperty,
isStringLiteral, isStringLiteral,
...@@ -16,11 +18,13 @@ import { camelize, capitalize, hyphenate } from '@vue/shared' ...@@ -16,11 +18,13 @@ import { camelize, capitalize, hyphenate } from '@vue/shared'
import { walk } from 'estree-walker' import { walk } from 'estree-walker'
import MagicString from 'magic-string' import MagicString from 'magic-string'
import { PluginContext } from 'rollup' import { PluginContext } from 'rollup'
import { M } from '../messages'
import { BINDING_COMPONENTS } from '../constants' import { BINDING_COMPONENTS } from '../constants'
import { normalizeMiniProgramFilename, removeExt } from '../utils' import { normalizeMiniProgramFilename, removeExt } from '../utils'
interface TransformVueComponentImportsOptions { interface TransformVueComponentImportsOptions {
root: string root: string
global?: boolean
resolve: PluginContext['resolve'] resolve: PluginContext['resolve']
dynamicImport: (name: string, source: string) => string dynamicImport: (name: string, source: string) => string
babelParserPlugins?: ParserPlugin[] babelParserPlugins?: ParserPlugin[]
...@@ -31,6 +35,7 @@ export async function transformVueComponentImports( ...@@ -31,6 +35,7 @@ export async function transformVueComponentImports(
{ {
root, root,
resolve, resolve,
global,
dynamicImport, dynamicImport,
babelParserPlugins, babelParserPlugins,
}: TransformVueComponentImportsOptions }: TransformVueComponentImportsOptions
...@@ -38,7 +43,7 @@ export async function transformVueComponentImports( ...@@ -38,7 +43,7 @@ export async function transformVueComponentImports(
code: string code: string
usingComponents: Record<string, string> usingComponents: Record<string, string>
}> { }> {
if (!code.includes(BINDING_COMPONENTS)) { if (!global && !code.includes(BINDING_COMPONENTS)) {
return { code, usingComponents: {} } return { code, usingComponents: {} }
} }
const s = new MagicString(code) const s = new MagicString(code)
...@@ -49,7 +54,9 @@ export async function transformVueComponentImports( ...@@ -49,7 +54,9 @@ export async function transformVueComponentImports(
const imports = findVueComponentImports( const imports = findVueComponentImports(
scriptAst.body, scriptAst.body,
parseComponents(scriptAst, findBindingComponents(scriptAst.body)) global
? parseGlobalComponents(scriptAst)
: parseComponents(scriptAst, findBindingComponents(scriptAst.body))
) )
const usingComponents: Record<string, string> = {} const usingComponents: Record<string, string> = {}
for (let i = 0; i < imports.length; i++) { for (let i = 0; i < imports.length; i++) {
...@@ -116,6 +123,47 @@ function findBindingComponents(ast: Statement[]): BindingComponents { ...@@ -116,6 +123,47 @@ function findBindingComponents(ast: Statement[]): BindingComponents {
} }
return {} return {}
} }
/**
* 查找全局组件定义:app.component('component-a',{})
* @param ast
* @returns
*/
function parseGlobalComponents(ast: Program) {
const bindingComponents: BindingComponents = {}
;(walk as any)(ast, {
enter(child: Node) {
if (!isCallExpression(child)) {
return
}
const { callee } = child
// .component
if (
!isMemberExpression(callee) ||
!isIdentifier(callee.property) ||
callee.property.name !== 'component'
) {
return
}
// .component('component-a',{})
const args = child.arguments
if (args.length !== 2) {
return
}
const [name, value] = args
if (!isStringLiteral(name)) {
return console.warn(M['mp.component.args[0]'])
}
if (!isIdentifier(value)) {
return console.warn(M['mp.component.args[1]'])
}
bindingComponents[value.name] = {
tag: name.value,
type: 'unknown',
}
},
})
return bindingComponents
}
/** /**
* 从 components 中查找定义的组件,修改 bindingComponents * 从 components 中查找定义的组件,修改 bindingComponents
* @param ast * @param ast
......
...@@ -13,6 +13,9 @@ function assert( ...@@ -13,6 +13,9 @@ function assert(
filename: 'foo.vue', filename: 'foo.vue',
prefixIdentifiers: true, prefixIdentifiers: true,
inline: true, inline: true,
generatorOpts: {
concise: true,
},
miniProgram: { miniProgram: {
slot: { slot: {
fallback: false, fallback: false,
......
...@@ -26,6 +26,9 @@ export function assert( ...@@ -26,6 +26,9 @@ export function assert(
inline: true, inline: true,
isNativeTag, isNativeTag,
isCustomElement, isCustomElement,
generatorOpts: {
concise: true,
},
miniProgram: { miniProgram: {
slot: { slot: {
fallback: false, fallback: false,
......
...@@ -16,7 +16,12 @@ function parseWithElementTransform( ...@@ -16,7 +16,12 @@ function parseWithElementTransform(
root: RootNode root: RootNode
node: ElementNode node: ElementNode
} { } {
const { ast, code, preamble } = compile(`<div>${template}</div>`, options) const { ast, code, preamble } = compile(`<div>${template}</div>`, {
generatorOpts: {
concise: true,
},
...options,
})
const node = (ast as any).children[0].children[0] const node = (ast as any).children[0].children[0]
return { return {
code, code,
...@@ -32,10 +37,14 @@ describe('compiler: element transform', () => { ...@@ -32,10 +37,14 @@ describe('compiler: element transform', () => {
source: `<image src="/static/logo.png"/>`, source: `<image src="/static/logo.png"/>`,
filename: 'foo.vue', filename: 'foo.vue',
id: 'foo', id: 'foo',
compiler: MPCompiler as unknown as TemplateCompiler, compiler: MPCompiler as unknown as TemplateCompiler,
compilerOptions: { compilerOptions: {
mode: 'module', mode: 'module',
generatorOpts: {
concise: true,
}, },
} as any,
transformAssetUrls: { transformAssetUrls: {
includeAbsolute: true, includeAbsolute: true,
...(createUniVueTransformAssetUrls('/') as Record<string, any>), ...(createUniVueTransformAssetUrls('/') as Record<string, any>),
......
...@@ -5,7 +5,12 @@ import { CompilerOptions } from '../src/options' ...@@ -5,7 +5,12 @@ import { CompilerOptions } from '../src/options'
import { assert, miniProgram } from './testUtils' import { assert, miniProgram } from './testUtils'
function parseWithVBind(template: string, options: CompilerOptions = {}) { function parseWithVBind(template: string, options: CompilerOptions = {}) {
const { ast, code } = compile(template, options) const { ast, code } = compile(template, {
generatorOpts: {
concise: true,
},
...options,
})
return { return {
code, code,
node: ast.children[0] as ElementNode, node: ast.children[0] as ElementNode,
......
...@@ -14,7 +14,12 @@ function parseWithForTransform( ...@@ -14,7 +14,12 @@ function parseWithForTransform(
template: string, template: string,
options: CompilerOptions = {} options: CompilerOptions = {}
) { ) {
const { ast } = compile(template, options) const { ast } = compile(template, {
generatorOpts: {
concise: true,
},
...options,
})
return { return {
root: ast, root: ast,
......
...@@ -9,7 +9,12 @@ function compileWithIfTransform( ...@@ -9,7 +9,12 @@ function compileWithIfTransform(
returnIndex: number = 0, returnIndex: number = 0,
childrenLen: number = 1 childrenLen: number = 1
) { ) {
const { ast } = compile(template, options) const { ast } = compile(template, {
generatorOpts: {
concise: true,
},
...options,
})
if (!options.onError) { if (!options.onError) {
expect(ast.children.length).toBe(childrenLen) expect(ast.children.length).toBe(childrenLen)
for (let i = 0; i < childrenLen; i++) { for (let i = 0; i < childrenLen; i++) {
......
...@@ -5,7 +5,12 @@ import { CompilerOptions } from '../src/options' ...@@ -5,7 +5,12 @@ import { CompilerOptions } from '../src/options'
import { assert } from './testUtils' import { assert } from './testUtils'
function parseWithVOn(template: string, options: CompilerOptions = {}) { function parseWithVOn(template: string, options: CompilerOptions = {}) {
const { ast } = compile(template, options) const { ast } = compile(template, {
generatorOpts: {
concise: true,
},
...options,
})
return { return {
root: ast, root: ast,
node: ast.children[0] as ElementNode, node: ast.children[0] as ElementNode,
......
...@@ -4,7 +4,12 @@ import { CompilerOptions } from '../src/options' ...@@ -4,7 +4,12 @@ import { CompilerOptions } from '../src/options'
import { assert } from './testUtils' import { assert } from './testUtils'
function parseWithVOn(template: string, options: CompilerOptions = {}) { function parseWithVOn(template: string, options: CompilerOptions = {}) {
const { ast } = compile(template, options) const { ast } = compile(template, {
generatorOpts: {
concise: true,
},
...options,
})
return { return {
root: ast, root: ast,
node: ast.children[0] as ElementNode, node: ast.children[0] as ElementNode,
......
...@@ -94,7 +94,9 @@ export function parseExpr( ...@@ -94,7 +94,9 @@ export function parseExpr(
code = genExpr(code) code = genExpr(code)
} }
try { try {
return parseExpression(code) return parseExpression(code, {
plugins: context.expressionPlugins,
})
} catch (e: any) { } catch (e: any) {
context.onError( context.onError(
createCompilerError( createCompilerError(
......
import { isString, isSymbol } from '@vue/shared' import { isString, isSymbol, hasOwn } from '@vue/shared'
import { import {
CodegenResult, CodegenResult,
CompoundExpressionNode, CompoundExpressionNode,
...@@ -11,7 +11,7 @@ import { ...@@ -11,7 +11,7 @@ import {
TO_DISPLAY_STRING, TO_DISPLAY_STRING,
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { Expression } from '@babel/types' import { Expression } from '@babel/types'
import { default as babelGenerate } from '@babel/generator' import { default as babelGenerate, GeneratorOptions } from '@babel/generator'
import { addImportDeclaration, matchEasycom } from '@dcloudio/uni-cli-shared' import { addImportDeclaration, matchEasycom } from '@dcloudio/uni-cli-shared'
import { CodegenOptions, CodegenRootNode } from './options' import { CodegenOptions, CodegenRootNode } from './options'
...@@ -88,7 +88,7 @@ export function generate( ...@@ -88,7 +88,7 @@ export function generate(
} }
push(`return `) push(`return `)
push(genBabelExpr(ast.renderData)) push(genBabelExpr(ast.renderData, options.generatorOpts))
if (useWithBlock) { if (useWithBlock) {
deindent() deindent()
push(`}`) push(`}`)
...@@ -283,13 +283,12 @@ function createGenNodeContext() { ...@@ -283,13 +283,12 @@ function createGenNodeContext() {
return context return context
} }
export function genBabelExpr(expr: Expression) { export function genBabelExpr(expr: Expression, opts: GeneratorOptions = {}) {
return babelGenerate(expr, { if (!hasOwn(opts, 'jsescOption')) {
concise: true, opts.jsescOption = {}
jsescOption: { }
quotes: 'single', opts.jsescOption!.quotes = 'single'
}, return babelGenerate(expr, opts).code
}).code
} }
export function genExpr( export function genExpr(
......
import { ParserPlugin } from '@babel/parser' import { ParserPlugin } from '@babel/parser'
import { GeneratorOptions } from '@babel/generator'
import { import {
CallExpression, CallExpression,
Expression, Expression,
...@@ -90,6 +91,7 @@ export interface CodegenOptions extends SharedTransformCodegenOptions { ...@@ -90,6 +91,7 @@ export interface CodegenOptions extends SharedTransformCodegenOptions {
scopeId?: string | null scopeId?: string | null
runtimeModuleName?: string runtimeModuleName?: string
runtimeGlobalName?: string runtimeGlobalName?: string
generatorOpts?: GeneratorOptions
miniProgram?: MiniProgramCompilerOptions miniProgram?: MiniProgramCompilerOptions
} }
......
...@@ -12,7 +12,11 @@ import { uniRenderjsPlugin } from './plugins/renderjs' ...@@ -12,7 +12,11 @@ import { uniRenderjsPlugin } from './plugins/renderjs'
export { UniMiniProgramPluginOptions } from './plugin' export { UniMiniProgramPluginOptions } from './plugin'
export default (options: UniMiniProgramPluginOptions) => { export default (options: UniMiniProgramPluginOptions) => {
return [ return [
uniMainJsPlugin(options), (options: {
vueOptions?: { script?: Partial<SFCScriptCompileOptions> }
}) => {
return uniMainJsPlugin(options.vueOptions?.script)
},
uniManifestJsonPlugin(options), uniManifestJsonPlugin(options),
uniPagesJsonPlugin(options), uniPagesJsonPlugin(options),
uniEntryPlugin(options), uniEntryPlugin(options),
......
import { defineUniMainJsPlugin } from '@dcloudio/uni-cli-shared' import {
import { UniMiniProgramPluginOptions } from '../plugin' addMiniProgramUsingComponents,
defineUniMainJsPlugin,
transformVueComponentImports,
} from '@dcloudio/uni-cli-shared'
import { SFCScriptCompileOptions } from '@vue/compiler-sfc'
import { dynamicImport } from './usingComponents'
export function uniMainJsPlugin(options: UniMiniProgramPluginOptions) { export function uniMainJsPlugin(
options: Partial<SFCScriptCompileOptions> = {}
) {
return defineUniMainJsPlugin((opts) => { return defineUniMainJsPlugin((opts) => {
return { return {
name: 'vite:uni-mp-main-js', name: 'vite:uni-mp-main-js',
enforce: 'pre', enforce: 'pre',
transform(code, id) { async transform(source, id) {
if (opts.filter(id)) { if (opts.filter(id)) {
code = code.includes('createSSRApp') source = source.includes('createSSRApp')
? createApp(code) ? createApp(source)
: createLegacyApp(code) : createLegacyApp(source)
const inputDir = process.env.UNI_INPUT_DIR
const { code, usingComponents } = await transformVueComponentImports(
source,
id,
{
root: inputDir,
global: true,
resolve: this.resolve,
dynamicImport,
babelParserPlugins: options.babelParserPlugins,
}
)
addMiniProgramUsingComponents('app', usingComponents)
return { return {
code: code:
`import 'plugin-vue:export-helper';import 'uni-mp-runtime';import './pages.json.js';` + `import 'plugin-vue:export-helper';import 'uni-mp-runtime';import './pages.json.js';` +
......
...@@ -49,6 +49,6 @@ export function uniUsingComponentsPlugin( ...@@ -49,6 +49,6 @@ export function uniUsingComponentsPlugin(
} }
} }
function dynamicImport(name: string, value: string) { export function dynamicImport(name: string, value: string) {
return `const ${name} = ()=>import('${virtualComponentPath(value)}')` return `const ${name} = ()=>import('${virtualComponentPath(value)}')`
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册