diff --git a/extensions/iceworks-time-steward/.vscodeignore b/extensions/iceworks-time-steward/.vscodeignore new file mode 100644 index 000000000..0bb532bdc --- /dev/null +++ b/extensions/iceworks-time-steward/.vscodeignore @@ -0,0 +1,11 @@ +.vscode/** +.vscode-test/** +out/test/** +src/** +.gitignore +vsc-extension-quickstart.md +**/tsconfig.json +**/tslint.json +**/*.map +**/*.ts +node_modules diff --git a/extensions/iceworks-time-steward/CHANGELOG.md b/extensions/iceworks-time-steward/CHANGELOG.md new file mode 100644 index 000000000..d5b1d2136 --- /dev/null +++ b/extensions/iceworks-time-steward/CHANGELOG.md @@ -0,0 +1,5 @@ +# Change Log + +## 0.1.0 + +- Initial release diff --git a/extensions/iceworks-time-steward/README.md b/extensions/iceworks-time-steward/README.md new file mode 100644 index 000000000..b27caad66 --- /dev/null +++ b/extensions/iceworks-time-steward/README.md @@ -0,0 +1,24 @@ +English | [简体中文](https://github.com/ice-lab/iceworks/blob/master/extensions/iceworks-time-steward/README.zh-CN.md) + +# Iceworks Time Steward(Beta) + +[![Version for VS Code Extension](https://vsmarketplacebadge.apphb.com/version-short/iceworks-team.iceworks-time-steward.svg?logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks-time-steward) +[![Installs](https://vsmarketplacebadge.apphb.com/installs-short/iceworks-team.iceworks-time-steward.svg)](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks-time-steward) +[![Rating](https://vsmarketplacebadge.apphb.com/rating-short/iceworks-team.iceworks-time-steward.svg)](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks-time-steward) +[![The MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT) + +Metrics, insights, and time tracking automatically generated from your programming activity. + +## Security and Privacy + +**We never access your code**: We do not process, send, or store your proprietary code. We only provide metrics about programming. Time Steward is an open source plugin, we make it easy to see the data we collect. + +**Your data is private**: We will never share your individually identifiable data with anyone. + +## Check Data + +This plugin is still in the internal testing stage, please contact the author to check the data. + +## More + +See the [Iceworks](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks) to know more features. diff --git a/extensions/iceworks-time-steward/README.zh-CN.md b/extensions/iceworks-time-steward/README.zh-CN.md new file mode 100644 index 000000000..ac9974b34 --- /dev/null +++ b/extensions/iceworks-time-steward/README.zh-CN.md @@ -0,0 +1,24 @@ +简体中文 | [English](https://github.com/ice-lab/iceworks/blob/master/extensions/iceworks-time-steward/README.md) + +# Iceworks 时间管家(Beta) + +[![Version for VS Code Extension](https://vsmarketplacebadge.apphb.com/version-short/iceworks-team.iceworks-time-steward.svg?logo=visual-studio-code)](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks-time-steward) +[![Installs](https://vsmarketplacebadge.apphb.com/installs-short/iceworks-team.iceworks-time-steward.svg)](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks-time-steward) +[![Rating](https://vsmarketplacebadge.apphb.com/rating-short/iceworks-team.iceworks-time-steward.svg)](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks-time-steward) +[![The MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT) + +自动生成你的编程时间统计和效率度量,提供优化建议。 + +## 如何查看数据 + +插件当前仍在内测阶段,数据查看需联系作者。 + +## 安全和隐私 + +**我们从不访问您的代码**:我们不处理、发送或存储您的代码,我们只提供有关编程的度量。时间管家是开源的,你可以很容易地看到我们收集了什么数据。 + +**您的数据是私有的**:我们永远不会与任何人共享您的个人数据。 + +## 更多 + +访问 [Iceworks](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks) 获取更多功能。 diff --git a/extensions/iceworks-time-steward/assets/logo.png b/extensions/iceworks-time-steward/assets/logo.png new file mode 100644 index 000000000..2b91c3379 Binary files /dev/null and b/extensions/iceworks-time-steward/assets/logo.png differ diff --git a/extensions/iceworks-time-steward/package.json b/extensions/iceworks-time-steward/package.json new file mode 100644 index 000000000..04e738bee --- /dev/null +++ b/extensions/iceworks-time-steward/package.json @@ -0,0 +1,54 @@ +{ + "name": "iceworks-time-steward", + "displayName": "Iceworks Time Steward(Beta)", + "description": "Metrics, insights, and time tracking automatically generated from your programming activity.", + "publisher": "iceworks-team", + "version": "0.1.0", + "engines": { + "vscode": "^1.41.0" + }, + "categories": [ + "Other" + ], + "keywords": [ + "Time", + "Metrics", + "Insights", + "Tracker", + "Time Tracking", + "Timer" + ], + "icon": "assets/logo.png", + "activationEvents": [ + "*" + ], + "repository": { + "type": "git", + "url": "https://github.com/ice-lab/iceworks.git" + }, + "main": "./build/extension.js", + "scripts": { + "vscode:prepublish": "rm -rf build && webpack --mode production", + "webpack": "webpack --mode development", + "webpack-dev": "webpack --mode development --watch", + "compile": "tsc -p ./tsconfig.json" + }, + "devDependencies": { + "@types/node": "^12.11.7", + "@types/vscode": "^1.41.0", + "@types/request": "^2.48.5", + "ts-loader": "^8.0.3", + "typescript": "^3.6.4", + "webpack": "^4.44.1", + "webpack-cli": "^3.3.12" + }, + "dependencies": { + "@iceworks/common-service": "^0.1.14", + "@iceworks/recorder": "^0.1.1" + }, + "homepage": "https://github.com/ice-lab/iceworks/blob/master/extensions/iceworks-time-steward/README.md", + "bugs": { + "url": "https://github.com/ice-lab/iceworks/issues", + "email": "iceworksteam@163.com" + } +} diff --git a/extensions/iceworks-time-steward/src/index.ts b/extensions/iceworks-time-steward/src/index.ts new file mode 100644 index 000000000..212245a0a --- /dev/null +++ b/extensions/iceworks-time-steward/src/index.ts @@ -0,0 +1,23 @@ +import * as vscode from 'vscode'; +import { getUserInfo, checkIsAliInternal } from '@iceworks/common-service'; +import { Timer } from './timer'; + +let timer: Timer; + +export async function activate() { + console.info('start timer'); + let user = { name: vscode.env.machineId }; + + const isAliInternal = await checkIsAliInternal(); + if (isAliInternal) { + user = await getUserInfo(); + } + + timer = new Timer(user); + timer.initialize(); +} + +export function deactivate() { + timer.dispose(); + console.info('timer has been disabled!'); +} diff --git a/extensions/iceworks-time-steward/src/timer.ts b/extensions/iceworks-time-steward/src/timer.ts new file mode 100755 index 000000000..4edf9d694 --- /dev/null +++ b/extensions/iceworks-time-steward/src/timer.ts @@ -0,0 +1,123 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import { Recorder } from '@iceworks/recorder'; +import { IPackageJson } from './typings/package'; + +// eslint-disable-next-line +const { name, version } = require('../package.json'); +const recorder = new Recorder(name, version); + +export class Timer { + private disposable: vscode.Disposable; + + private lastFile: string = ''; + + private lastHeartbeat: number = 0; + + private user; + + constructor(user) { + this.user = user; + } + + public initialize(): void { + this.setupEventListeners(); + } + + private setupEventListeners(): void { + // subscribe to selection change and editor activation events + const subscriptions: vscode.Disposable[] = []; + + // 监听光标位置变化 + vscode.window.onDidChangeTextEditorSelection(this.onChange, this, subscriptions); + // 监听左侧文件树当前激活的文件变化 + vscode.window.onDidChangeActiveTextEditor(this.onChange, this, subscriptions); + // 监听保存事件 + vscode.workspace.onDidSaveTextDocument(this.onSave, this, subscriptions); + // 监听编辑器失去焦点的变化情况 + vscode.window.onDidChangeWindowState(this.onFocus, this, subscriptions); + + // create a combined disposable from both event subscriptions + this.disposable = vscode.Disposable.from(...subscriptions); + } + + public dispose() { + this.disposable.dispose(); + } + + private onChange(): void { + this.onEvent(false); + } + + private onSave(): void { + this.onEvent(true); + } + + private onFocus(event: { focused: boolean }): void { + if (!event.focused) { + this.onEvent(true); + this.lastHeartbeat = 0; + } + } + + private onEvent(isWrite: boolean): void { + const editor = vscode.window.activeTextEditor; + if (editor) { + const doc = editor.document; + if (doc) { + const file: string = doc.fileName; + if (file) { + const time: number = Date.now(); + const enoughTimePassed = this.enoughTimePassed(time); + console.info(`isWrite:${isWrite}; enoughTimePassed: ${enoughTimePassed}; lastFile: ${this.lastFile}`); + /** + * 发送规则: + * 1. 保存时必然发送 + * 2. 切换文件时必然发送 + * 3. 在同一个文件里面每隔2分钟发送一次 + */ + if (isWrite || enoughTimePassed || this.lastFile !== file) { + const project = this.getProjectName(file); + const subTime = time - this.lastHeartbeat; + const { name } = this.user; + if (this.lastHeartbeat !== 0) { + recorder.record({ + module: 'main', + action: 'tracking', + data: { + user: name, + timestamp: subTime, + project, + }, + }); + } + + this.lastFile = file; + this.lastHeartbeat = time; + } + } + } + } + } + + private enoughTimePassed(time: number): boolean { + return this.lastHeartbeat + 120000 < time; + } + + private getProjectName(file: string): string { + const uri = vscode.Uri.file(file); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri); + if (workspaceFolder) { + try { + const packageJsonResultObj: IPackageJson = JSON.parse( + fs.readFileSync(`${workspaceFolder.uri.path}/package.json`, 'utf-8') + ); + return packageJsonResultObj.name; + } catch (e) { + console.error(e.message); + } + return workspaceFolder.name; + } + return ''; + } +} diff --git a/extensions/iceworks-time-steward/src/typings/package.ts b/extensions/iceworks-time-steward/src/typings/package.ts new file mode 100644 index 000000000..ecb8cbaca --- /dev/null +++ b/extensions/iceworks-time-steward/src/typings/package.ts @@ -0,0 +1,3 @@ +export interface IPackageJson { + name: string; +} diff --git a/extensions/iceworks-time-steward/tsconfig.extension.json b/extensions/iceworks-time-steward/tsconfig.extension.json new file mode 100644 index 000000000..587fcaa70 --- /dev/null +++ b/extensions/iceworks-time-steward/tsconfig.extension.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2019", + "lib": ["ES2019"], + "sourceMap": true, + "strict": true /* enable all strict type-checking options */ + } +} diff --git a/extensions/iceworks-time-steward/tsconfig.json b/extensions/iceworks-time-steward/tsconfig.json new file mode 100644 index 000000000..fc726d65d --- /dev/null +++ b/extensions/iceworks-time-steward/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": "out", + "moduleResolution": "node", + "lib": ["es2018"], + "sourceMap": true, + "rootDir": ".", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strict": true, + "importHelpers": true, + "removeComments": true, + "noImplicitAny": false, + "strictPropertyInitialization": false + }, + "exclude": ["node_modules"] +} diff --git a/extensions/iceworks-time-steward/webpack.config.js b/extensions/iceworks-time-steward/webpack.config.js new file mode 100644 index 000000000..d19a102d6 --- /dev/null +++ b/extensions/iceworks-time-steward/webpack.config.js @@ -0,0 +1,44 @@ +const path = require('path'); + +const tsConfigPath = path.join(__dirname, 'tsconfig.json'); + +const config = { + target: 'node', + entry: './src/index.ts', + output: { + path: path.resolve(__dirname, 'build'), + filename: 'extension.js', + libraryTarget: 'commonjs2', + devtoolModuleFilenameTemplate: '../[resource-path]', + }, + externals: { + vscode: 'commonjs vscode', + }, + resolve: { + extensions: ['.ts', '.js'], + }, + module: { + rules: [ + { + test: /\.ts$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + configFile: tsConfigPath, + }, + }, + ], + }, + ], + }, +}; + +module.exports = (env, argv) => { + if (argv.mode === 'development') { + config.devtool = 'source-map'; + } + + return config; +}; diff --git a/extensions/iceworks-ui-builder/CHANGELOG.md b/extensions/iceworks-ui-builder/CHANGELOG.md index 90f951a5c..d6d7130f8 100644 --- a/extensions/iceworks-ui-builder/CHANGELOG.md +++ b/extensions/iceworks-ui-builder/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 0.1.14 + +- fix: fail to use lazy to import page component in `routes.js` +- fix: `import React from 'react';` occurs in Rax page +- fix: the page component name in `routes.js` has capital error + ## 0.1.13 - docs: update text for find component and generate page diff --git a/extensions/iceworks-ui-builder/package.json b/extensions/iceworks-ui-builder/package.json index 66863a124..5730fdbfe 100644 --- a/extensions/iceworks-ui-builder/package.json +++ b/extensions/iceworks-ui-builder/package.json @@ -3,7 +3,7 @@ "displayName": "Iceworks UI Builder", "description": "Build UI by low-code way", "publisher": "iceworks-team", - "version": "0.1.13", + "version": "0.1.14", "engines": { "vscode": "^1.41.0" }, diff --git a/extensions/iceworks-ui-builder/web/src/pages/PageCreator/configForm.tsx b/extensions/iceworks-ui-builder/web/src/pages/PageCreator/configForm.tsx index e4aae407c..f05800de0 100644 --- a/extensions/iceworks-ui-builder/web/src/pages/PageCreator/configForm.tsx +++ b/extensions/iceworks-ui-builder/web/src/pages/PageCreator/configForm.tsx @@ -94,15 +94,18 @@ export default ({ setIsCreating(true); let pageIndexPath = ''; try { - pageIndexPath = await callService('page', 'createPage', { + const result = await callService('page', 'createPage', { ...selectedPage, pageName: values.pageName, templateData, }); + pageIndexPath = result.pageIndexPath; + const { pageName } = result; + if (isConfigurableRouter) { try { - await callService('router', 'create', values); + await callService('router', 'create', { ...values, pageName }); } catch (error) { Notification.error({ content: error.message }); } diff --git a/extensions/iceworks-ui-builder/web/src/pages/PageGenerater/index.tsx b/extensions/iceworks-ui-builder/web/src/pages/PageGenerater/index.tsx index 267de3f4d..329f1c0d2 100644 --- a/extensions/iceworks-ui-builder/web/src/pages/PageGenerater/index.tsx +++ b/extensions/iceworks-ui-builder/web/src/pages/PageGenerater/index.tsx @@ -6,6 +6,7 @@ import { LocaleProvider } from '@/i18n'; import { useIntl, FormattedMessage } from 'react-intl'; import { IMaterialData } from '@iceworks/material-utils'; import RouterDetailForm from '@/components/RouterDetailForm'; +import * as upperCamelCase from 'uppercamelcase'; import PageSelected from './components/PageSelected'; import callService from '../../callService'; import styles from './index.module.scss'; @@ -118,14 +119,17 @@ const Home = () => { setIsCreating(true); let pageIndexPath = ''; try { - pageIndexPath = await callService('page', 'generate', { + const result = await callService('page', 'generate', { blocks: selectedBlocks, pageName: values.pageName, }); + pageIndexPath = result.pageIndexPath; + const { pageName } = result; + if (isConfigurableRouter) { try { - await callService('router', 'create', values); + await callService('router', 'create', { ...values, pageName }); } catch (error) { Notification.error({ content: error.message }); } diff --git a/packages/page-service/package.json b/packages/page-service/package.json index 51add294e..aaa305887 100644 --- a/packages/page-service/package.json +++ b/packages/page-service/package.json @@ -1,6 +1,6 @@ { "name": "@iceworks/page-service", - "version": "0.1.13", + "version": "0.1.14", "description": "Iceworks page service for VSCode extension.", "files": [ "lib" diff --git a/packages/page-service/src/index.ts b/packages/page-service/src/index.ts index 67fae0465..16f5a4350 100644 --- a/packages/page-service/src/index.ts +++ b/packages/page-service/src/index.ts @@ -22,6 +22,7 @@ import * as upperCamelCase from 'uppercamelcase'; import * as ejs from 'ejs'; import * as transfromTsToJs from 'transform-ts-to-js'; import reactPageTemplate from './templates/template.react'; +import raxPageTemplate from './templates/template.rax'; import vuePageTemplate from './templates/template.vue'; import i18n from './i18n'; import renderEjsTemplates from './utils/renderEjsTemplates'; @@ -52,11 +53,21 @@ export const generate = async function ({ const isVueProjectFramework = projectFramework === 'vue'; const projectLanguageType = await getProjectLanguageType(); const fileName = isVueProjectFramework ? 'index.vue' : `index.${projectLanguageType}x`; - const dist = path.join(pagePath, fileName); + const pageIndexPath = path.join(pagePath, fileName); try { await addBlocks(blocks, pageName); - const fileStr = isVueProjectFramework ? vuePageTemplate : reactPageTemplate; + + // get page ejs template + let fileStr = ''; + if (projectFramework === 'icejs') { + fileStr = reactPageTemplate; + } else if (projectFramework === 'vue') { + fileStr = vuePageTemplate; + } else if (projectFramework === 'rax-app') { + fileStr = raxPageTemplate; + } + const fileContent = ejs.compile(fileStr)({ blocks: blocks.map((block: any) => { const blockName = upperCamelCase(block.name); @@ -76,13 +87,13 @@ export const generate = async function ({ parser: prettierParserType, }); - await fse.writeFile(dist, rendered, 'utf-8'); + await fse.writeFile(pageIndexPath, rendered, 'utf-8'); } catch (error) { remove(pageName); throw error; } - return dist; + return { pageIndexPath, pageName }; } }; diff --git a/packages/page-service/src/templates/template.rax.ts b/packages/page-service/src/templates/template.rax.ts new file mode 100644 index 000000000..a9b95d193 --- /dev/null +++ b/packages/page-service/src/templates/template.rax.ts @@ -0,0 +1,17 @@ +const templateStr = `import { createElement } from 'rax'; +<% for(var i = 0; i < blocks.length; i++) { -%> +import <%= blocks[i].className %> from '<%= blocks[i].relativePath -%>'; +<% } -%> + +export default function () { + return ( +
+ <% for(var i = 0; i < blocks.length; i++){ -%> + <% if (blocks[i].description) { %>{/* <%= blocks[i].description -%> */}<% } %> + <<%= blocks[i].className -%> /> + <% } -%> +
+ ); +} +`; +export default templateStr; diff --git a/packages/router-service/package.json b/packages/router-service/package.json index 1f4e28bed..edfc8a4a8 100644 --- a/packages/router-service/package.json +++ b/packages/router-service/package.json @@ -1,6 +1,6 @@ { "name": "@iceworks/router-service", - "version": "0.1.0", + "version": "0.1.1", "description": "Iceworks router service for VSCode extension.", "files": [ "lib" diff --git a/packages/router-service/src/index.ts b/packages/router-service/src/index.ts index 4ee52b86c..8b739bdde 100644 --- a/packages/router-service/src/index.ts +++ b/packages/router-service/src/index.ts @@ -200,6 +200,9 @@ function changeImportDeclarations(routerConfigAST, data) { // router import page or layout have @ let existAtSign = false; let existLazy = false; + // React.lazy(): the existLazyPrefix is true + // lazy(): the existLazyPrefix is false + let existLazyPrefix = false; traverse(routerConfigAST, { ImportDeclaration: ({ node, key }) => { @@ -234,17 +237,20 @@ function changeImportDeclarations(routerConfigAST, data) { const code = generate(node.declarations[0]).code; // parse const declaration to get directory type (layouts or pages) // support three path types - // 1. const xxx = React.lazy(() => import('pages/xxx')); - // 2. const xxx = React.lazy(() => import('./pages/xxx')); - // 3. const xxx = React.lazy(() => import('@/pages/xxx')); - const noPrefixReg = /(\w+)\s=\sReact\.lazy(.+)import\(['|"]((\w+)\/.+)['|"]\)/; - const hasPrefixReg = /(\w+)\s=\sReact\.lazy(.+)import\(['|"]((\.|@)\/(\w+)\/.+)['|"]\)/; + // 1. const xxx = (React.)?lazy(() => import('pages/xxx')); + // 2. const xxx = (React.)?lazy(() => import('./pages/xxx')); + // 3. const xxx = (React.)?lazy(() => import('@/pages/xxx')); + const noPrefixReg = /(\w+)\s=\s(React\.)?lazy(.+)import\(['|"]((\w+)\/.+)['|"]\)/; + const hasPrefixReg = /(\w+)\s=\s(React\.)?lazy(.+)import\(['|"]((\.|@)\/(\w+)\/.+)['|"]\)/; const matchLazyReg = noPathPrefix ? noPrefixReg : hasPrefixReg; - const idx = noPathPrefix ? 4 : 5; + const idx = noPathPrefix ? 5 : 6; const match = code.match(matchLazyReg); if (match && match.length > idx) { existLazy = true; + if (match[2]) { + existLazyPrefix = true; + } existAtSign = match[idx - 1] === '@'; importDeclarations.push({ index: key, @@ -350,7 +356,7 @@ function changeImportDeclarations(routerConfigAST, data) { importCode += `import ${name} from '${sign}/${type}/${name}';\n`; } else { // use lazy `const Page = React.lazy(() => import('@/pages/Page'))` - lazyCode += `const ${name} = React.lazy(() => import('${sign}/${type}/${name}'));\n`; + lazyCode += `const ${name} = ${existLazyPrefix ? 'React.' : ''}lazy(() => import('${sign}/${type}/${name}'));\n`; } });