From 0114e258692c5ffe07d5257f86d5a01de94f19bd Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Wed, 18 Aug 2021 11:52:50 +0800 Subject: [PATCH] feat(app): console --- packages/uni-app-vite/src/index.ts | 11 +++ .../src/babel/plugin/ConsolePlugin.ts | 64 +++++++++++++ .../__snapshots__/console.spec.ts.snap | 21 ++++ .../uni-cli-shared/__tests__/console.spec.ts | 51 ++++++++++ packages/uni-cli-shared/src/env/provide.ts | 1 + packages/uni-cli-shared/src/hbx/console.ts | 95 ------------------- packages/uni-cli-shared/src/hbx/formatLog.ts | 63 ++++++++++++ packages/uni-cli-shared/src/logs/console.ts | 44 +++++++++ .../src/vite/plugins/console.ts | 22 +++++ .../uni-cli-shared/src/vite/plugins/index.ts | 1 + .../uni-cli-shared/src/vite/plugins/inject.ts | 21 +--- packages/uni-cli-shared/src/vite/utils/ast.ts | 15 +++ packages/uni-cli-shared/src/vite/utils/url.ts | 15 +++ 13 files changed, 311 insertions(+), 113 deletions(-) create mode 100644 packages/uni-cli-nvue/src/babel/plugin/ConsolePlugin.ts create mode 100644 packages/uni-cli-shared/__tests__/__snapshots__/console.spec.ts.snap create mode 100644 packages/uni-cli-shared/__tests__/console.spec.ts delete mode 100644 packages/uni-cli-shared/src/hbx/console.ts create mode 100644 packages/uni-cli-shared/src/hbx/formatLog.ts create mode 100644 packages/uni-cli-shared/src/logs/console.ts diff --git a/packages/uni-app-vite/src/index.ts b/packages/uni-app-vite/src/index.ts index 2a71f3df7..ca951aebe 100644 --- a/packages/uni-app-vite/src/index.ts +++ b/packages/uni-app-vite/src/index.ts @@ -1,9 +1,11 @@ +import path from 'path' import { initProvide, uniViteInjectPlugin, uniCssScopedPlugin, getAppStyleIsolation, parseManifestJsonOnce, + uniConsolePlugin, } from '@dcloudio/uni-cli-shared' import { UniAppPlugin } from './plugin' import { uniTemplatePlugin } from './plugins/template' @@ -31,6 +33,15 @@ function initUniCssScopedPluginOptions() { const plugins = [ // uniResolveIdPlugin(), + uniConsolePlugin({ + filename(filename) { + filename = path.relative(process.env.UNI_INPUT_DIR, filename) + if (filename.startsWith('.')) { + return '' + } + return filename + }, + }), uniMainJsPlugin(), uniManifestJsonPlugin(), uniPagesJsonPlugin(), diff --git a/packages/uni-cli-nvue/src/babel/plugin/ConsolePlugin.ts b/packages/uni-cli-nvue/src/babel/plugin/ConsolePlugin.ts new file mode 100644 index 000000000..b5882efc4 --- /dev/null +++ b/packages/uni-cli-nvue/src/babel/plugin/ConsolePlugin.ts @@ -0,0 +1,64 @@ +import * as Types from '@babel/types' +import { PluginObj, PluginPass } from '@babel/core' +import { normalizePath } from '@dcloudio/uni-cli-shared' + +const METHODS = ['error', 'warn', 'info', 'log', 'debug'] +const FORMAT_LOG = '__f__' +module.exports = function ({ + types: t, +}: { + types: typeof Types +}): PluginObj< + { opts: { filename?: (filename: string) => string | undefined } } & PluginPass +> { + return { + visitor: { + CallExpression(path, state) { + let { + opts, + file: { + opts: { filename }, + }, + } = state + if (!filename) { + return + } + if (opts && opts.filename) { + filename = opts.filename(filename) + } + if (!filename) { + return + } + const { callee, arguments: args, loc } = path.node + if (!t.isMemberExpression(callee)) { + return + } + const { object, property } = callee + if (!t.isIdentifier(object) || !t.isIdentifier(property)) { + return + } + if (object.name !== 'console' || !METHODS.includes(property.name)) { + return + } + if (property.name === 'debug') { + property.name = 'log' + } + const arg = args[0] + if ( + arg && + t.isCallExpression(arg) && + t.isIdentifier(arg.callee) && + arg.callee.name === FORMAT_LOG + ) { + return + } + args.unshift({ + type: 'StringLiteral', + value: ` at ${normalizePath(filename)}:${loc!.start.line}`, + } as Types.StringLiteral) + args.unshift(t.stringLiteral(property.name)) + path.replaceWith(t.callExpression(t.identifier(FORMAT_LOG), args)) + }, + }, + } +} diff --git a/packages/uni-cli-shared/__tests__/__snapshots__/console.spec.ts.snap b/packages/uni-cli-shared/__tests__/__snapshots__/console.spec.ts.snap new file mode 100644 index 000000000..29524fec7 --- /dev/null +++ b/packages/uni-cli-shared/__tests__/__snapshots__/console.spec.ts.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`console console.debug 1`] = `"__f__('info','at foo.vue:1',a,b,c);"`; + +exports[`console console.error 1`] = `"__f__('info','at foo.vue:1',a,b,c);"`; + +exports[`console console.info 1`] = `"__f__('info','at foo.vue:1',a,b,c);"`; + +exports[`console console.log 1`] = `"const a = 1;__f__('log','at foo.vue:1',a);"`; + +exports[`console console.log multiline 1`] = ` +"const a = 1; + +__f__('log','at foo.vue:3',a); +const b = 2 +__f__('log','at foo.vue:5',a,b); +__f__('log','at foo.vue:6',a,b,c); +" +`; + +exports[`console console.warn 1`] = `"__f__('info','at foo.vue:1',a,b,c);"`; diff --git a/packages/uni-cli-shared/__tests__/console.spec.ts b/packages/uni-cli-shared/__tests__/console.spec.ts new file mode 100644 index 000000000..2f361fdf6 --- /dev/null +++ b/packages/uni-cli-shared/__tests__/console.spec.ts @@ -0,0 +1,51 @@ +import { rewriteConsoleExpr } from '../src/logs/console' +import { normalizeLog } from '../src/hbx/formatLog' +const filename = 'foo.vue' +describe('console', () => { + test('console.log', () => { + expect( + rewriteConsoleExpr(filename, `const a = 1;console.log(a);`) + ).toMatchSnapshot() + }) + test('console.log multiline', () => { + expect( + rewriteConsoleExpr( + filename, + `const a = 1; + +console.log(a); +const b = 2 +console.log(a,b); +console.log(a,b,c); +` + ) + ).toMatchSnapshot() + }) + test('console.info', () => { + expect( + rewriteConsoleExpr(filename, `console.info(a,b,c);`) + ).toMatchSnapshot() + }) + test('console.debug', () => { + expect( + rewriteConsoleExpr(filename, `console.info(a,b,c);`) + ).toMatchSnapshot() + }) + test('console.warn', () => { + expect( + rewriteConsoleExpr(filename, `console.info(a,b,c);`) + ).toMatchSnapshot() + }) + test('console.error', () => { + expect( + rewriteConsoleExpr(filename, `console.info(a,b,c);`) + ).toMatchSnapshot() + }) + test('console.log format', () => { + expect( + normalizeLog('log', 'at ' + filename + ':1', ['a', 'b', { a: 1 }]) + ).toBe( + `a---COMMA---b---COMMA------BEGIN:JSON---{"a":1}---END:JSON--- at foo.vue:1` + ) + }) +}) diff --git a/packages/uni-cli-shared/src/env/provide.ts b/packages/uni-cli-shared/src/env/provide.ts index 783e2097b..7094e16eb 100644 --- a/packages/uni-cli-shared/src/env/provide.ts +++ b/packages/uni-cli-shared/src/env/provide.ts @@ -3,6 +3,7 @@ const libDir = path.resolve(__dirname, '../lib') export function initProvide() { const cryptoDefine = [path.join(libDir, 'crypto.js'), 'default'] return { + __f__: [path.join(__dirname, '../hbx/formatLog.js'), 'formatLog'], crypto: cryptoDefine, 'window.crypto': cryptoDefine, 'global.crypto': cryptoDefine, diff --git a/packages/uni-cli-shared/src/hbx/console.ts b/packages/uni-cli-shared/src/hbx/console.ts deleted file mode 100644 index b999ab1fa..000000000 --- a/packages/uni-cli-shared/src/hbx/console.ts +++ /dev/null @@ -1,95 +0,0 @@ -function typof(v: unknown) { - var s = Object.prototype.toString.call(v) - return s.substring(8, s.length - 1) -} - -function isDebugMode() { - // @ts-expect-error - return typeof __channelId__ === 'string' && __channelId__ -} - -function jsonStringifyReplacer(key: string, value: unknown) { - switch (typof(value)) { - case 'Function': - return 'function() { [native code] }' - default: - return value - } -} - -type LogType = 'log' | 'info' | 'warn' | 'error' - -// export function log(type: LogType) { -// for ( -// var _len = arguments.length, -// args = new Array(_len > 1 ? _len - 1 : 0), -// _key = 1; -// _key < _len; -// _key++ -// ) { -// args[_key - 1] = arguments[_key] -// } -// console[type].apply(console, args) -// } - -export default function formatLog() { - for ( - var _len = arguments.length, args = new Array(_len), _key = 0; - _key < _len; - _key++ - ) { - args[_key] = arguments[_key] - } - var type = args.shift() as LogType - if (isDebugMode()) { - args.push(args.pop().replace('at ', 'uni-app:///')) - return console[type].apply(console, args) - } - - var msgs = args.map(function (v) { - var type = Object.prototype.toString.call(v).toLowerCase() - - if (type === '[object object]' || type === '[object array]') { - try { - v = - '---BEGIN:JSON---' + - JSON.stringify(v, jsonStringifyReplacer) + - '---END:JSON---' - } catch (e) { - v = type - } - } else { - if (v === null) { - v = '---NULL---' - } else if (v === undefined) { - v = '---UNDEFINED---' - } else { - var vType = typof(v).toUpperCase() - - if (vType === 'NUMBER' || vType === 'BOOLEAN') { - v = '---BEGIN:' + vType + '---' + v + '---END:' + vType + '---' - } else { - v = String(v) - } - } - } - - return v - }) - var msg = '' - - if (msgs.length > 1) { - var lastMsg = msgs.pop() - msg = msgs.join('---COMMA---') - - if (lastMsg.indexOf(' at ') === 0) { - msg += lastMsg - } else { - msg += '---COMMA---' + lastMsg - } - } else { - msg = msgs[0] - } - - console[type](msg) -} diff --git a/packages/uni-cli-shared/src/hbx/formatLog.ts b/packages/uni-cli-shared/src/hbx/formatLog.ts new file mode 100644 index 000000000..9ceb62c33 --- /dev/null +++ b/packages/uni-cli-shared/src/hbx/formatLog.ts @@ -0,0 +1,63 @@ +import { toTypeString, toRawType } from '@vue/shared' + +function isDebugMode() { + // @ts-expect-error + return typeof __channelId__ === 'string' && __channelId__ +} + +function jsonStringifyReplacer(k: string, p: unknown) { + switch (toRawType(p)) { + case 'Function': + return 'function() { [native code] }' + default: + return p + } +} + +export function normalizeLog( + type: 'log' | 'info' | 'debug' | 'warn' | 'error', + filename: string, + args: unknown[] +) { + if (isDebugMode()) { + args.push(filename.replace('at ', 'uni-app:///')) + return console[type].apply(console, args) + } + + const msgs = args.map(function (v) { + const type = toTypeString(v).toLowerCase() + if (type === '[object object]' || type === '[object array]') { + try { + v = + '---BEGIN:JSON---' + + JSON.stringify(v, jsonStringifyReplacer) + + '---END:JSON---' + } catch (e) { + v = type + } + } else { + if (v === null) { + v = '---NULL---' + } else if (v === undefined) { + v = '---UNDEFINED---' + } else { + const vType = toRawType(v).toUpperCase() + if (vType === 'NUMBER' || vType === 'BOOLEAN') { + v = '---BEGIN:' + vType + '---' + v + '---END:' + vType + '---' + } else { + v = String(v) + } + } + } + return v + }) + return msgs.join('---COMMA---') + ' ' + filename +} + +export function formatLog( + type: 'log' | 'info' | 'debug' | 'warn' | 'error', + filename: string, + ...args: unknown[] +) { + console[type](normalizeLog(type, filename, args)) +} diff --git a/packages/uni-cli-shared/src/logs/console.ts b/packages/uni-cli-shared/src/logs/console.ts new file mode 100644 index 000000000..b885eb8f7 --- /dev/null +++ b/packages/uni-cli-shared/src/logs/console.ts @@ -0,0 +1,44 @@ +import { MagicString } from '@vue/compiler-sfc' + +const F = '__f__' +export function rewriteConsoleExpr(filename: string, code: string) { + const re = /(console\.(log|info|debug|warn|error))\(([^)]+)\)/g + const locate = getLocator(code) + const s = new MagicString(code) + let match: RegExpExecArray | null + while ((match = re.exec(code))) { + const [, expr, type] = match + s.overwrite( + match.index, + match.index + expr.length + 1, + F + `('${type}','at ${filename}:${locate(match.index).line + 1}',` + ) + } + return s.toString() +} + +function getLocator(source: string) { + const originalLines = source.split('\n') + const lineOffsets: number[] = [] + + for (let i = 0, pos = 0; i < originalLines.length; i++) { + lineOffsets.push(pos) + pos += originalLines[i].length + 1 + } + + return function locate(index: number) { + let i = 0 + let j = lineOffsets.length + while (i < j) { + const m = (i + j) >> 1 + if (index < lineOffsets[m]) { + j = m + } else { + i = m + 1 + } + } + const line = i - 1 + const column = index - lineOffsets[line] + return { line, column } + } +} diff --git a/packages/uni-cli-shared/src/vite/plugins/console.ts b/packages/uni-cli-shared/src/vite/plugins/console.ts index 008bfda78..aaab73952 100644 --- a/packages/uni-cli-shared/src/vite/plugins/console.ts +++ b/packages/uni-cli-shared/src/vite/plugins/console.ts @@ -1,20 +1,42 @@ +import debug from 'debug' import { Plugin } from 'vite' import { createFilter, FilterPattern } from '@rollup/pluginutils' +import { isJsFile, parseVueRequest } from '../utils' +import { rewriteConsoleExpr } from '../../logs/console' + export interface ConsoleOptions { + filename?: (filename: string) => string include?: FilterPattern exclude?: FilterPattern } +const debugConsole = debug('vite:uni:console') + export function uniConsolePlugin(options: ConsoleOptions): Plugin { const filter = createFilter(options.include, options.exclude) return { name: 'vite:uni-app-console', + enforce: 'pre', + apply: 'build', transform(code, id) { if (!filter(id)) return null + if (!isJsFile(id)) return null + let { filename } = parseVueRequest(id) + if (options.filename) { + filename = options.filename(filename) + } + if (!filename) { + return null + } if (!code.includes('console.')) { return null } + debugConsole(id) + return { + code: rewriteConsoleExpr(filename, code), + map: null, + } }, } } diff --git a/packages/uni-cli-shared/src/vite/plugins/index.ts b/packages/uni-cli-shared/src/vite/plugins/index.ts index a312e675e..003bde7cf 100644 --- a/packages/uni-cli-shared/src/vite/plugins/index.ts +++ b/packages/uni-cli-shared/src/vite/plugins/index.ts @@ -4,6 +4,7 @@ export * from './copy' export * from './inject' export * from './mainJs' export * from './jsonJs' +export * from './console' export { assetPlugin } from './vitejs/plugins/asset' export { cssPlugin, cssPostPlugin } from './vitejs/plugins/css' diff --git a/packages/uni-cli-shared/src/vite/plugins/inject.ts b/packages/uni-cli-shared/src/vite/plugins/inject.ts index 344a5aa44..eb519da57 100644 --- a/packages/uni-cli-shared/src/vite/plugins/inject.ts +++ b/packages/uni-cli-shared/src/vite/plugins/inject.ts @@ -1,4 +1,4 @@ -import path, { sep } from 'path' +import { sep } from 'path' import debug from 'debug' import { Plugin } from 'vite' @@ -16,14 +16,7 @@ import { walk } from 'estree-walker' import { extend } from '@vue/shared' import { MagicString } from '@vue/compiler-sfc' -import { EXTNAME_JS_RE, EXTNAME_VUE } from '../../constants' - -import { - isProperty, - isReference, - isMemberExpression, - parseVueRequest, -} from '../utils' +import { isProperty, isReference, isMemberExpression, isJsFile } from '../utils' interface Scope { parent: Scope @@ -79,15 +72,7 @@ export function uniViteInjectPlugin(options: InjectOptions): Plugin { name: 'vite:uni-inject', transform(code, id) { if (!filter(id)) return null - const isJs = EXTNAME_JS_RE.test(id) - if (!isJs) { - const { filename, query } = parseVueRequest(id) - const isVueJs = - EXTNAME_VUE.includes(path.extname(filename)) && !query.vue - if (!isVueJs) { - return null - } - } + if (!isJsFile(id)) return null debugInjectTry(id) if (code.search(firstpass) === -1) return null if (sep !== '/') id = id.split(sep).join('/') diff --git a/packages/uni-cli-shared/src/vite/utils/ast.ts b/packages/uni-cli-shared/src/vite/utils/ast.ts index 84e44dc83..5ec385062 100644 --- a/packages/uni-cli-shared/src/vite/utils/ast.ts +++ b/packages/uni-cli-shared/src/vite/utils/ast.ts @@ -63,6 +63,21 @@ export function createLiteral(value: string) { } as Literal } +export function createIdentifier(name: string) { + return { + type: 'Identifier', + name, + } as Identifier +} + +export function createCallExpression(callee: unknown, args: unknown[]) { + return { + type: 'CallExpression', + callee, + arguments: args, + } as CallExpression +} + export function parseVue(code: string, errors: SyntaxError[]) { return parse(code, { isNativeTag: () => true, diff --git a/packages/uni-cli-shared/src/vite/utils/url.ts b/packages/uni-cli-shared/src/vite/utils/url.ts index b56972cca..83fed59ef 100644 --- a/packages/uni-cli-shared/src/vite/utils/url.ts +++ b/packages/uni-cli-shared/src/vite/utils/url.ts @@ -1,4 +1,6 @@ +import path from 'path' import qs from 'querystring' +import { EXTNAME_JS_RE, EXTNAME_VUE } from '../../constants' export interface VueQuery { vue?: boolean @@ -39,3 +41,16 @@ export const hashRE = /#.*$/ export const cleanUrl = (url: string) => url.replace(hashRE, '').replace(queryRE, '') + +export function isJsFile(id: string) { + const isJs = EXTNAME_JS_RE.test(id) + if (isJs) { + return true + } + const { filename, query } = parseVueRequest(id) + const isVueJs = EXTNAME_VUE.includes(path.extname(filename)) && !query.vue + if (isVueJs) { + return true + } + return false +} -- GitLab