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

wip(uts): compiler

上级 444a0e1c
{"version":3,"sources":["/Users/fxy/DCloud/test-uts/uni_modules/test-uts1/utssdk/app-ios/index.uts"],"sourcesContent":["export function test(): string {\n return 1;\n}\n"],"names":[],"mappings":";AAAO,KAAS,UAAQ,MAAM,CAAC;IAC7B,OAAO,CAAC;AACV;;;;mCAFwB,MAAM;eAAd"}
\ No newline at end of file
......@@ -14,7 +14,7 @@ const utssdkPluginDir = 'utssdk/test-uts'
const uniModulesPluginDir = 'uni_modules/test-uniplugin'
describe('sourceMap', () => {
describe('uts:sourceMap', () => {
test('resolveUtsPluginSourceMapFile with uni_modules uts=>kotlin', () => {
const sourceMapFile = resolveUtsPluginSourceMapFile(
'kotlin',
......@@ -135,11 +135,17 @@ describe('sourceMap', () => {
filename,
line: 3,
column: 15,
outputDir,
})
expect(res).toEqual({
line: 18,
column: 16,
lastColumn: null,
source: resolve(
outputDir,
uniModulesPluginDir,
'utssdk/app-android/index.kt'
),
})
})
test('originalPositionFor', async () => {
......@@ -156,7 +162,6 @@ describe('sourceMap', () => {
)
const { line, column, source } = await originalPositionFor({
sourceMapFile,
filename,
line: 18,
column: 16,
})
......
import path from 'path'
import { parseUtsSwiftPluginStacktrace } from '../src/stacktrace'
const stacktrace = `/uts-development-ios/dependences/buildFramework/template/xcode_ust_template/unimoduleTestUts1/src/index.swift:3:12: error: cannot convert return expression of type 'Int' to return type 'String'\n return 1;\n ^\nnote: Building targets in dependency order\n/Applications/HBuilderX-Alpha.app/Contents/HBuilderX/plugins/uts-development-ios/dependences/buildFramework/template/xcode_ust_template/unimoduleTestUts1/UTS.xcodeproj: warning: The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 9.0, but the range of supported deployment target versions is 11.0 to 16.1.99. (in target 'unimoduleTestUts1' from project 'UTS')\n`
describe('uts:stacktrace', () => {
test('parseUtsSwiftPluginStacktrace', async () => {
const codes = await parseUtsSwiftPluginStacktrace({
stacktrace,
sourceMapFile: path.resolve(
__dirname,
'./examples/sourcemap/index.swift.map'
),
sourceRoot: '/Users/fxy/DCloud/test-uts',
})
expect(codes).toContain(
`uni_modules/test-uts1/utssdk/app-ios/index.uts:2:10`
)
})
})
......@@ -5,6 +5,8 @@ import { runSwiftProd, runSwiftDev } from './swift'
import { genProxyCode, resolvePlatformIndex, resolveRootIndex } from './code'
import { resolvePackage } from './utils'
import { parseUtsSwiftPluginStacktrace } from './stacktrace'
import { resolveUtsPluginSourceMapFile } from './sourceMap'
export * from './sourceMap'
......@@ -59,24 +61,32 @@ export async function compile(module: string) {
// 添加其他文件的依赖
deps.push(...res.deps)
}
if (res.type === 'kotlin') {
const files: string[] = []
if (process.env.UNI_APP_CHANGED_DEX_FILES) {
try {
files.push(...JSON.parse(process.env.UNI_APP_CHANGED_DEX_FILES))
} catch (e) {}
}
if (res.dex) {
files.push(res.dex)
}
process.env.UNI_APP_CHANGED_DEX_FILES = JSON.stringify([
...new Set(files),
])
} else if (res.type === 'swift') {
if (code) {
throw res.msg
if (res.type === 'swift') {
if (res.code) {
throw await parseUtsSwiftPluginStacktrace({
stacktrace: res.msg,
sourceMapFile: resolveUtsPluginSourceMapFile(
'swift',
filename,
process.env.UNI_INPUT_DIR,
process.env.UNI_OUTPUT_DIR
),
sourceRoot: process.env.UNI_INPUT_DIR,
})
}
}
const files: string[] = []
if (process.env.UNI_APP_UTS_CHANGED_FILES) {
try {
files.push(...JSON.parse(process.env.UNI_APP_UTS_CHANGED_FILES))
} catch (e) {}
}
if (res.changed) {
files.push(...res.changed)
}
process.env.UNI_APP_UTS_CHANGED_FILES = JSON.stringify([
...new Set(files),
])
}
}
}
......
......@@ -72,7 +72,10 @@ export async function runKotlinProd(filename: string) {
})
}
export type RunKotlinDevResult = UtsResult & { type: 'kotlin'; dex?: string }
export type RunKotlinDevResult = UtsResult & {
type: 'kotlin'
changed: string[]
}
export async function runKotlinDev(
filename: string
......@@ -84,6 +87,7 @@ export async function runKotlinDev(
const result = (await compile(filename)) as RunKotlinDevResult
result.type = 'kotlin'
result.changed = []
const kotlinFile = resolveUTSPlatformFile(filename, {
inputDir: process.env.UNI_INPUT_DIR,
......@@ -142,9 +146,9 @@ export async function runKotlinDev(
} catch (e) {}
const dexFile = resolveDexFile(jarFile)
if (fs.existsSync(dexFile)) {
result.dex = normalizePath(
path.relative(process.env.UNI_OUTPUT_DIR, dexFile)
)
result.changed = [
normalizePath(path.relative(process.env.UNI_OUTPUT_DIR, dexFile)),
]
}
} else {
throw `${normalizePath(
......
......@@ -76,6 +76,7 @@ interface PositionFor {
filename: string
line: number
column: number
withSourceContent?: boolean
}
const consumers: Record<
......@@ -93,13 +94,25 @@ export function generatedPositionFor({
filename,
line,
column,
}: PositionFor): Promise<NullablePosition> {
outputDir,
}: PositionFor & { outputDir?: string }): Promise<
NullablePosition & { source: string | null }
> {
return resolveSourceMapConsumer(sourceMapFile).then((consumer) => {
return consumer.generatedPositionFor({
source: (isWindows ? `\\\\?\\` : '') + filename,
const res = consumer.generatedPositionFor({
source: (isWindows ? `\\\\?\\` : '') + normalizePath(filename),
line,
column,
})
let source = null
if (outputDir) {
// 根据 sourceMapFile 和 outputDir,计算出生成后的文件路径
source = join(
outputDir,
relative(join(outputDir, '../.sourcemap/app'), sourceMapFile)
).replace('.map', '')
}
return Object.assign(res, { source })
})
}
......@@ -109,14 +122,25 @@ export function generatedPositionFor({
* @returns
*/
export function originalPositionFor(
generatedPosition: PositionFor
): Promise<NullableMappedPosition> {
generatedPosition: Omit<PositionFor, 'filename'>
): Promise<NullableMappedPosition & { sourceContent?: string }> {
return resolveSourceMapConsumer(generatedPosition.sourceMapFile).then(
(consumer) => {
return consumer.originalPositionFor({
const res = consumer.originalPositionFor({
line: generatedPosition.line,
column: generatedPosition.column,
})
if (
generatedPosition.withSourceContent &&
res.source &&
res.line &&
res.column
) {
return Object.assign(res, {
sourceContent: consumer.sourceContentFor(res.source, true),
})
}
return res
}
)
}
......@@ -137,3 +161,15 @@ async function resolveSourceMapConsumer(sourceMapFile: string) {
}
return consumers[sourceMapFile].consumer
}
function normalizePath(path: string) {
return isWindows ? unixPathToWindowsPath(path) : windowsPathToUnixPath(path)
}
function windowsPathToUnixPath(path: string) {
return path.replace(/\\/g, '/')
}
function unixPathToWindowsPath(path: string) {
return path.replace(/\//g, '\\')
}
import path from 'path'
import { normalizePath } from './shared'
import { originalPositionFor } from './sourceMap'
const splitRE = /\r?\n/
const uniModulesUtsRe =
/\/unimodule(.*)\/src\/index.swift:([0-9]+):([0-9]+):\s+error:\s+(.*)/
interface ParseUtsPluginStacktraceOptions {
stacktrace: string
sourceRoot: string
sourceMapFile: string
}
export async function parseUtsSwiftPluginStacktrace({
stacktrace,
sourceRoot,
sourceMapFile,
}: ParseUtsPluginStacktraceOptions) {
const res: string[] = []
const lines = stacktrace.split(splitRE)
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const codes = await parseUtsStacktraceLine(
line,
uniModulesUtsRe,
sourceMapFile,
sourceRoot
)
if (codes) {
res.push(...codes)
i++ // 移除下一行的 code frame
} else {
res.push(line)
}
}
return res.join('\n')
}
async function parseUtsStacktraceLine(
lineStr: string,
re: RegExp,
sourceMapFile: string,
sourceRoot: string
) {
const uniModulesMatches = lineStr.match(re)
if (!uniModulesMatches) {
return
}
const lines: string[] = []
const [, , line, column, message] = uniModulesMatches
const originalPosition = await originalPositionFor({
sourceMapFile,
line: parseInt(line),
column: parseInt(column),
withSourceContent: true,
})
if (originalPosition.source && originalPosition.sourceContent) {
lines.push(`${message}`)
lines.push(
`at ${normalizePath(
path.relative(sourceRoot, originalPosition.source)
)}:${originalPosition.line}:${originalPosition.column}`
)
if (originalPosition.line !== null && originalPosition.column !== null) {
lines.push(
generateCodeFrame(originalPosition.sourceContent, {
line: originalPosition.line,
column: originalPosition.column,
})
)
}
}
return lines
}
const range: number = 2
function posToNumber(
source: string,
pos: number | { line: number; column: number }
): number {
if (typeof pos === 'number') return pos
const lines = source.split(splitRE)
const { line, column } = pos
let start = 0
for (let i = 0; i < line - 1; i++) {
start += lines[i].length + 1
}
return start + column
}
export function generateCodeFrame(
source: string,
start: number | { line: number; column: number } = 0,
end?: number
): string {
start = posToNumber(source, start)
end = end || start
const lines = source.split(splitRE)
let count = 0
const res: string[] = []
for (let i = 0; i < lines.length; i++) {
count += lines[i].length + 1
if (count >= start) {
for (let j = i - range; j <= i + range || end > count; j++) {
if (j < 0 || j >= lines.length) continue
const line = j + 1
res.push(
`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}| ${
lines[j]
}`
)
const lineLength = lines[j].length
if (j === i) {
// push underline
const pad = start - (count - lineLength) + 1
const length = Math.max(
1,
end > count ? lineLength - pad : end - start
)
res.push(` | ` + ' '.repeat(pad) + '^'.repeat(length))
} else if (j > i) {
if (end > count) {
const length = Math.max(Math.min(end - count, lineLength), 1)
res.push(` | ` + '^'.repeat(length))
}
count += lineLength + 1
}
}
break
}
}
return res.join('\n')
}
......@@ -45,6 +45,7 @@ export type RunSwiftDevResult = UtsResult & {
type: 'swift'
code: number
msg: string
changed: string[]
}
export async function runSwiftDev(filename: string) {
......@@ -62,6 +63,7 @@ export async function runSwiftDev(filename: string) {
platform: 'app-ios',
extname: '.swift',
})
result.changed = []
// 开发模式下,需要生成 framework
if (fs.existsSync(swiftFile)) {
const compilerServer = getCompilerServer<SwiftCompilerServer>(
......@@ -92,6 +94,7 @@ export async function runSwiftDev(filename: string) {
})
result.code = code
result.msg = msg
result.changed = [swiftFile]
}
return result
}
......
......@@ -177,7 +177,9 @@ export function getCompilerServer<T extends CompilerServer>(
): T | undefined {
const compilerServerPath = path.resolve(
process.env.UNI_HBUILDERX_PLUGINS,
`${pluginName}/out/external`
`${pluginName}/out/${
pluginName === 'uniapp-runextension' ? 'main.js' : 'external.js'
}`
)
if (fs.existsSync(compilerServerPath)) {
// eslint-disable-next-line no-restricted-globals
......
......@@ -39,7 +39,7 @@ export async function runDev(options: CliOptions & ServerOptions) {
// 首次全量同步
process.env.UNI_APP_CHANGED_PAGES = ''
process.env.UNI_APP_CHANGED_FILES = ''
process.env.UNI_APP_CHANGED_DEX_FILES = ''
process.env.UNI_APP_UTS_CHANGED_FILES = ''
return (
(isFirstEnd = false),
output('log', M['dev.watching.end']),
......@@ -48,11 +48,11 @@ export async function runDev(options: CliOptions & ServerOptions) {
}
const files = process.env.UNI_APP_CHANGED_FILES
const pages = process.env.UNI_APP_CHANGED_PAGES
const dex = process.env.UNI_APP_CHANGED_DEX_FILES
const dex = process.env.UNI_APP_UTS_CHANGED_FILES
const changedFiles = pages || files
process.env.UNI_APP_CHANGED_PAGES = ''
process.env.UNI_APP_CHANGED_FILES = ''
process.env.UNI_APP_CHANGED_DEX_FILES = ''
process.env.UNI_APP_UTS_CHANGED_FILES = ''
if (
(changedFiles && !changedFiles.includes(APP_CONFIG_SERVICE)) ||
dex
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册