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

wip(uts): compiler

上级 4cfe9d7d
...@@ -8,7 +8,11 @@ export function genTemplate( ...@@ -8,7 +8,11 @@ export function genTemplate(
options: CompilerOptions options: CompilerOptions
) { ) {
if (!template) { if (!template) {
return { code: genRenderFunctionDecl(options) + ` { return null }` } return {
code: genRenderFunctionDecl(options) + ` { return null }`,
importEasyComponents: [],
importUTSComponents: [],
}
} }
return compile(template.content, options) return compile(template.content, options)
} }
...@@ -44,12 +44,15 @@ import { ...@@ -44,12 +44,15 @@ import {
TO_HANDLERS, TO_HANDLERS,
} from './runtimeHelpers' } from './runtimeHelpers'
import { object2Map } from './utils' import { object2Map } from './utils'
import { matchEasycom, parseUTSComponent } from '@dcloudio/uni-cli-shared'
type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
export interface CodegenContext extends Required<CodegenOptions> { export interface CodegenContext extends Required<CodegenOptions> {
source: string source: string
code: string code: string
importEasyComponents: string[]
importUTSComponents: string[]
line: number line: number
column: number column: number
offset: number offset: number
...@@ -82,6 +85,8 @@ function createCodegenContext( ...@@ -82,6 +85,8 @@ function createCodegenContext(
filename, filename,
source: ast.loc.source, source: ast.loc.source,
code: ``, code: ``,
importEasyComponents: [],
importUTSComponents: [],
column: 1, column: 1,
line: 1, line: 1,
offset: 0, offset: 0,
...@@ -191,6 +196,8 @@ export function generate( ...@@ -191,6 +196,8 @@ export function generate(
} }
return { return {
code: context.code, code: context.code,
importEasyComponents: context.importEasyComponents,
importUTSComponents: context.importUTSComponents,
// SourceMapGenerator does have toJSON() method but it's not in the types // SourceMapGenerator does have toJSON() method but it's not in the types
map: context.map ? (context.map as any).toJSON() : undefined, map: context.map ? (context.map as any).toJSON() : undefined,
} }
...@@ -199,7 +206,14 @@ export function generate( ...@@ -199,7 +206,14 @@ export function generate(
function genAssets( function genAssets(
assets: string[], assets: string[],
type: 'component' | 'directive', type: 'component' | 'directive',
{ helper, push, newline }: CodegenContext {
helper,
push,
newline,
targetLanguage,
importEasyComponents,
importUTSComponents,
}: CodegenContext
) { ) {
const resolver = helper( const resolver = helper(
type === 'component' ? RESOLVE_COMPONENT : RESOLVE_DIRECTIVE type === 'component' ? RESOLVE_COMPONENT : RESOLVE_DIRECTIVE
...@@ -211,11 +225,42 @@ function genAssets( ...@@ -211,11 +225,42 @@ function genAssets(
if (maybeSelfReference) { if (maybeSelfReference) {
id = id.slice(0, -6) id = id.slice(0, -6)
} }
push( let assetCode = ''
`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${ if (type === 'component') {
// 原生UTS组件
const utsComponentOptions = parseUTSComponent(id, targetLanguage)
if (utsComponentOptions) {
assetCode = `const ${toValidAssetId(id, type)} = ${
utsComponentOptions.namespace
}.${utsComponentOptions.className}.name`
const importCode = `import '${utsComponentOptions.source}'`
if (!importUTSComponents.includes(importCode)) {
importUTSComponents.push(importCode)
}
}
if (!assetCode) {
const source = matchEasycom(id)
if (source) {
const componentId = toValidAssetId(id, type)
assetCode = `const ${componentId} = ${resolver}(${JSON.stringify(
id
)}${maybeSelfReference ? `, true` : ``})`
const importCode = `import ${componentId} from '${source}'`
if (!importEasyComponents.includes(importCode)) {
importEasyComponents.push(importCode)
}
}
}
}
if (!assetCode) {
assetCode = `const ${toValidAssetId(
id,
type
)} = ${resolver}(${JSON.stringify(id)}${
maybeSelfReference ? `, true` : `` maybeSelfReference ? `, true` : ``
})` })`
) }
push(assetCode)
if (i < assets.length - 1) { if (i < assets.length - 1) {
newline() newline()
} }
......
...@@ -77,5 +77,7 @@ export type CompilerOptions = TransformOptions & CodegenOptions ...@@ -77,5 +77,7 @@ export type CompilerOptions = TransformOptions & CodegenOptions
export interface CodegenResult { export interface CodegenResult {
code: string code: string
importEasyComponents: string[]
importUTSComponents: string[]
map?: RawSourceMap map?: RawSourceMap
} }
...@@ -186,6 +186,8 @@ export async function transformVue( ...@@ -186,6 +186,8 @@ export async function transformVue(
const fileName = path.relative(options.root, filename) const fileName = path.relative(options.root, filename)
const className = genClassName(fileName, options.classNamePrefix) const className = genClassName(fileName, options.classNamePrefix)
let templateCode = '' let templateCode = ''
let templateImportEasyComponentsCode = ''
let templateImportUTSComponentsCode = ''
if (!isApp) { if (!isApp) {
const templateResult = genTemplate(descriptor, { const templateResult = genTemplate(descriptor, {
targetLanguage: options.targetLanguage as any, targetLanguage: options.targetLanguage as any,
...@@ -195,6 +197,10 @@ export async function transformVue( ...@@ -195,6 +197,10 @@ export async function transformVue(
sourceMap: true, sourceMap: true,
}) })
templateCode = templateResult.code templateCode = templateResult.code
templateImportEasyComponentsCode =
templateResult.importEasyComponents.join('\n')
templateImportUTSComponentsCode =
templateResult.importUTSComponents.join('\n')
} }
// 生成 script 文件 // 生成 script 文件
let utsCode = let utsCode =
...@@ -203,7 +209,8 @@ export async function transformVue( ...@@ -203,7 +209,8 @@ export async function transformVue(
genStyle(descriptor, { filename: fileName, className }) + genStyle(descriptor, { filename: fileName, className }) +
'\n' '\n'
utsCode += templateCode utsCode += templateCode
let jsCode = '' let jsCode =
templateImportEasyComponentsCode + '\n' + templateImportUTSComponentsCode
const content = descriptor.script?.content const content = descriptor.script?.content
if (content) { if (content) {
jsCode += await parseImports(content) jsCode += await parseImports(content)
......
...@@ -10,24 +10,28 @@ describe('easycom', () => { ...@@ -10,24 +10,28 @@ describe('easycom', () => {
initEasycoms(rootDir, { platform: 'h5', dirs: [] }).easycoms initEasycoms(rootDir, { platform: 'h5', dirs: [] }).easycoms
).toEqual([ ).toEqual([
{ {
name: 'test',
pattern: new RegExp('^test$'), pattern: new RegExp('^test$'),
replacement: normalizePath( replacement: normalizePath(
path.resolve(rootDir, 'components/test/test.vue') path.resolve(rootDir, 'components/test/test.vue')
), ),
}, },
{ {
name: 'test1',
pattern: new RegExp('^test1$'), pattern: new RegExp('^test1$'),
replacement: normalizePath( replacement: normalizePath(
path.resolve(rootDir, 'components/test1/test1.vue') path.resolve(rootDir, 'components/test1/test1.vue')
), ),
}, },
{ {
name: 'test2',
pattern: new RegExp('^test2$'), pattern: new RegExp('^test2$'),
replacement: normalizePath( replacement: normalizePath(
path.resolve(rootDir, 'uni_modules/plugin/components/test2/test2.vue') path.resolve(rootDir, 'uni_modules/plugin/components/test2/test2.vue')
), ),
}, },
{ {
name: '^uni-(.*)',
pattern: new RegExp('^uni-(.*)'), pattern: new RegExp('^uni-(.*)'),
replacement: normalizePath( replacement: normalizePath(
path.resolve(rootDir, 'components/uni-$1.vue') path.resolve(rootDir, 'components/uni-$1.vue')
......
...@@ -19,6 +19,7 @@ interface EasycomOption { ...@@ -19,6 +19,7 @@ interface EasycomOption {
custom?: EasycomCustom custom?: EasycomCustom
} }
export interface EasycomMatcher { export interface EasycomMatcher {
name: string
pattern: RegExp pattern: RegExp
replacement: string replacement: string
} }
...@@ -70,9 +71,7 @@ export function initEasycoms( ...@@ -70,9 +71,7 @@ export function initEasycoms(
const options = initEasycomOptions(parsePagesJsonOnce(inputDir, platform)) const options = initEasycomOptions(parsePagesJsonOnce(inputDir, platform))
const initUTSEasycom = () => { const initUTSEasycom = () => {
initUTSComponents(inputDir, platform).forEach((item) => { initUTSComponents(inputDir, platform).forEach((item) => {
const index = easycoms.findIndex( const index = easycoms.findIndex((easycom) => item.name === easycom.name)
(easycom) => item.pattern.toString() === easycom.pattern.toString()
)
if (index > -1) { if (index > -1) {
easycoms.splice(index, 1, item) easycoms.splice(index, 1, item)
} else { } else {
...@@ -148,6 +147,8 @@ function initEasycom({ ...@@ -148,6 +147,8 @@ function initEasycom({
} }
Object.keys(easycomsObj).forEach((name) => { Object.keys(easycomsObj).forEach((name) => {
easycoms.push({ easycoms.push({
name:
name.startsWith('^') && name.endsWith('$') ? name.slice(1, -1) : name,
pattern: new RegExp(name), pattern: new RegExp(name),
replacement: easycomsObj[name], replacement: easycomsObj[name],
}) })
......
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import glob from 'fast-glob' import glob from 'fast-glob'
import { camelize, capitalize } from '@vue/shared'
import * as UTSCompiler from '@dcloudio/uni-uts-v1' import * as UTSCompiler from '@dcloudio/uni-uts-v1'
import { isInHBuilderX } from './hbx' import { isInHBuilderX } from './hbx'
...@@ -128,20 +128,54 @@ export function resolveUTSCompiler(): typeof UTSCompiler { ...@@ -128,20 +128,54 @@ export function resolveUTSCompiler(): typeof UTSCompiler {
return require(compilerPath) return require(compilerPath)
} }
interface UTSComponentMeta {
source: string
kotlinPackage: string
swiftModule: string
}
const utsComponents = new Map<string, UTSComponentMeta>()
export function isUTSComponent(name: string) {
return utsComponents.has(name)
}
export function parseUTSComponent(name: string, type: 'kotlin' | 'swift') {
const meta = utsComponents.get(name)
if (meta) {
const namespace =
meta[type === 'swift' ? 'swiftModule' : 'kotlinPackage'] || ''
const className = capitalize(camelize(name)) + 'Component'
return {
className,
namespace,
source: meta.source,
}
}
}
export function initUTSComponents( export function initUTSComponents(
inputDir: string, inputDir: string,
platform: UniApp.PLATFORM platform: UniApp.PLATFORM
): EasycomMatcher[] { ): EasycomMatcher[] {
utsComponents.clear()
const components: EasycomMatcher[] = [] const components: EasycomMatcher[] = []
if (platform !== 'app' && platform !== 'app-plus') { if (platform !== 'app' && platform !== 'app-plus') {
return components return components
} }
const easycomsObj = Object.create(null) const easycomsObj: Record<
string,
{ source: string; kotlinPackage: string; swiftModule: string }
> = {}
const dirs = resolveUTSComponentDirs(inputDir) const dirs = resolveUTSComponentDirs(inputDir)
dirs.forEach((dir) => { dirs.forEach((dir) => {
const is_uni_modules_utssdk = dir.endsWith('utssdk') const is_uni_modules_utssdk = dir.endsWith('utssdk')
const is_ussdk = const is_ussdk =
!is_uni_modules_utssdk && path.dirname(dir).endsWith('utssdk') !is_uni_modules_utssdk && path.dirname(dir).endsWith('utssdk')
const pluginId = is_uni_modules_utssdk
? path.basename(path.dirname(dir))
: path.basename(dir)
if (is_uni_modules_utssdk || is_ussdk) { if (is_uni_modules_utssdk || is_ussdk) {
glob glob
.sync('**/*.vue', { .sync('**/*.vue', {
...@@ -161,15 +195,33 @@ export function initUTSComponents( ...@@ -161,15 +195,33 @@ export function initUTSComponents(
const importDir = normalizePath( const importDir = normalizePath(
is_uni_modules_utssdk ? path.dirname(dir) : dir is_uni_modules_utssdk ? path.dirname(dir) : dir
) )
easycomsObj[`^${name}$`] = `${importDir}?uts-proxy` easycomsObj[`^${name}$`] = {
source: `${importDir}?uts-proxy`,
kotlinPackage: parseKotlinPackageWithPluginId(
pluginId,
is_uni_modules_utssdk
),
swiftModule: parseSwiftPackageWithPluginId(
pluginId,
is_uni_modules_utssdk
),
}
} }
}) })
} }
}) })
Object.keys(easycomsObj).forEach((name) => { Object.keys(easycomsObj).forEach((name) => {
const obj = easycomsObj[name]
const componentName = name.slice(1, -1)
components.push({ components.push({
name: componentName,
pattern: new RegExp(name), pattern: new RegExp(name),
replacement: easycomsObj[name], replacement: obj.source,
})
utsComponents.set(componentName, {
source: obj.source,
kotlinPackage: obj.kotlinPackage,
swiftModule: obj.swiftModule,
}) })
}) })
return components return components
...@@ -201,3 +253,31 @@ function parseVueComponentName(file: string) { ...@@ -201,3 +253,31 @@ function parseVueComponentName(file: string) {
return matches[1] return matches[1]
} }
} }
function prefix(id: string) {
if (
process.env.UNI_UTS_MODULE_PREFIX &&
!id.startsWith(process.env.UNI_UTS_MODULE_PREFIX)
) {
return process.env.UNI_UTS_MODULE_PREFIX + '-' + id
}
return id
}
export function parseKotlinPackageWithPluginId(
id: string,
is_uni_modules: boolean
) {
return 'uts.sdk.' + (is_uni_modules ? 'modules.' : '') + camelize(prefix(id))
}
export function parseSwiftPackageWithPluginId(
id: string,
is_uni_modules: boolean
) {
return (
'UTSSDK' +
(is_uni_modules ? 'Modules' : '') +
capitalize(camelize(prefix(id)))
)
}
package uts.sdk.modules.testUts;
import kotlinx.coroutines.async;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.Deferred;
import kotlinx.coroutines.Dispatchers;
import io.dcloud.uts.Map;
import io.dcloud.uts.UTSAndroid;
import io.dcloud.uts.*;
fun test() {}
fun testByJs() {
return test();
}
...@@ -189,7 +189,7 @@ export async function runKotlinDev( ...@@ -189,7 +189,7 @@ export async function runKotlinDev(
kotlinFile, kotlinFile,
jarFile, jarFile,
getKotlincHome(), getKotlincHome(),
getDefaultJar() (isX ? getDefaultJar(2) : getDefaultJar())
.concat(resolveLibs(filename)) .concat(resolveLibs(filename))
.concat(deps) .concat(deps)
.concat(resDeps) .concat(resDeps)
...@@ -349,7 +349,7 @@ export async function compile( ...@@ -349,7 +349,7 @@ export async function compile(
if (rClass) { if (rClass) {
imports.push(rClass) imports.push(rClass)
} }
const componentsCode = genComponentsCode(filename, components) const componentsCode = genComponentsCode(filename, components, isX)
const { package: pluginPackage, id: pluginId } = parseKotlinPackage(filename) const { package: pluginPackage, id: pluginId } = parseKotlinPackage(filename)
const input: Parameters<typeof bundle>[1]['input'] = { const input: Parameters<typeof bundle>[1]['input'] = {
root: inputDir, root: inputDir,
...@@ -371,8 +371,7 @@ export async function compile( ...@@ -371,8 +371,7 @@ export async function compile(
return return
} }
} }
const options = {
const result = await bundle(UTSTarget.KOTLIN, {
input, input,
output: { output: {
isX, isX,
...@@ -388,7 +387,8 @@ export async function compile( ...@@ -388,7 +387,8 @@ export async function compile(
uniExtApiPackage: 'io.dcloud.uts.extapi', uniExtApiPackage: 'io.dcloud.uts.extapi',
}, },
}, },
}) }
const result = await bundle(UTSTarget.KOTLIN, options)
sourceMap && sourceMap &&
moveRootIndexSourceMap(filename, { moveRootIndexSourceMap(filename, {
inputDir, inputDir,
......
...@@ -177,11 +177,11 @@ function isCliProject(projectPath: string) { ...@@ -177,11 +177,11 @@ function isCliProject(projectPath: string) {
export async function compile( export async function compile(
filename: string, filename: string,
{ inputDir, outputDir, sourceMap, components, isPlugin }: ToSwiftOptions { inputDir, outputDir, sourceMap, components, isX, isPlugin }: ToSwiftOptions
) { ) {
const { bundle, UTSTarget } = getUTSCompiler() const { bundle, UTSTarget } = getUTSCompiler()
// let time = Date.now() // let time = Date.now()
const componentsCode = genComponentsCode(filename, components) const componentsCode = genComponentsCode(filename, components, isX)
const { namespace, id: pluginId } = parseSwiftPackage(filename) const { namespace, id: pluginId } = parseSwiftPackage(filename)
const input: Parameters<typeof bundle>[1]['input'] = { const input: Parameters<typeof bundle>[1]['input'] = {
root: inputDir, root: inputDir,
...@@ -206,6 +206,7 @@ export async function compile( ...@@ -206,6 +206,7 @@ export async function compile(
const result = await bundle(UTSTarget.SWIFT, { const result = await bundle(UTSTarget.SWIFT, {
input, input,
output: { output: {
isX,
isPlugin, isPlugin,
outDir: outputDir, outDir: outputDir,
package: namespace, package: namespace,
......
...@@ -280,16 +280,18 @@ function parseVueComponentName(file: string) { ...@@ -280,16 +280,18 @@ function parseVueComponentName(file: string) {
export function genComponentsCode( export function genComponentsCode(
filename: string, filename: string,
components: Record<string, string> components: Record<string, string>,
isX: boolean
) { ) {
const codes: string[] = [] const codes: string[] = []
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
Object.keys(components).forEach((name) => { Object.keys(components).forEach((name) => {
const source = normalizePath(path.relative(dirname, components[name])) const source = normalizePath(path.relative(dirname, components[name]))
const className = capitalize(camelize(name))
codes.push( codes.push(
`export { default as ${capitalize(camelize(name))}Component } from '${ `export { default as ${className}Component${
source.startsWith('.') ? source : './' + source isX ? `, ${className}Node` : ''
}'` } } from '${source.startsWith('.') ? source : './' + source}'`
) )
}) })
return codes.join('\n') return codes.join('\n')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册