diff --git a/.eslintrc.js b/.eslintrc.js index ea17c0334..5d752f2f4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,8 +24,6 @@ const tsRules = deepmerge(tslint, { }, rules: { ...commonRules, - '@typescript-eslint/array-type': 0, - '@typescript-eslint/no-non-null-assertion': 0, '@typescript-eslint/no-inferrable-types': 1, '@typescript-eslint/ban-ts-ignore': 0, '@typescript-eslint/array-type': 0, diff --git a/extensions/iceworks-app/CHANGELOG.md b/extensions/iceworks-app/CHANGELOG.md index 9a0516724..8b63517bf 100644 --- a/extensions/iceworks-app/CHANGELOG.md +++ b/extensions/iceworks-app/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## 0.4.4 +- feat: add Iceworks debug. + ## 0.4.3 - fix: build error diff --git a/extensions/iceworks-app/README.en.md b/extensions/iceworks-app/README.en.md index 45efa1ba7..9c31b0ed2 100644 --- a/extensions/iceworks-app/README.en.md +++ b/extensions/iceworks-app/README.en.md @@ -90,6 +90,12 @@ If you install [Iceworks suite](https://marketplace.visualstudio.com/items?ItemN > NOTE: if you do not find the Iceworks icon, Activate Extension the Iceworks plugin by following the action in 'Activate Extension'. 2. Click the Iceworks plugin icon to enter the Iceworks command palette. +### Debug + +![debug-demo](https://img.alicdn.com/tfs/TB1vCixhP39YK4jSZPcXXXrUFXa-1200-695.gif) + +For more information, please see [Reference Document](https://github.com/ice-lab/iceworks/blob/master/extensions/iceworks-app/docs/debug.en.md) + ## More See the [Iceworks Pack](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks) to know more features. diff --git a/extensions/iceworks-app/README.md b/extensions/iceworks-app/README.md index 1ea96703a..eba1b3b37 100644 --- a/extensions/iceworks-app/README.md +++ b/extensions/iceworks-app/README.md @@ -89,6 +89,12 @@ > 注意 : 如果没有 Iceworks 图标,请按照 `激活` 章节的内容来激活此按钮。 2. 点击 Iceworks 按钮,打开命令面板。 +### 工程调试 + +![debug-demo](https://img.alicdn.com/tfs/TB1vCixhP39YK4jSZPcXXXrUFXa-1200-695.gif) + +更多请看[参考文档](https://github.com/ice-lab/iceworks/blob/master/extensions/iceworks-app/docs/debug.md) + ## 更多 访问 [Iceworks 套件](https://marketplace.visualstudio.com/items?itemName=iceworks-team.iceworks)获取更多功能。 diff --git a/extensions/iceworks-app/docs/debug.en.md b/extensions/iceworks-app/docs/debug.en.md new file mode 100644 index 000000000..58982118d --- /dev/null +++ b/extensions/iceworks-app/docs/debug.en.md @@ -0,0 +1,85 @@ +# Iceworks Debug + +[简体中文]((./debug.md)) | English + +Use VS Code and Chrome to debug the source code. + +![debug-demo](https://img.alicdn.com/tfs/TB1vCixhP39YK4jSZPcXXXrUFXa-1200-695.gif) + +Click the debug button in the upper right corner of the file to start debugging. Will insert .vscode/launch.json and .vscode/tasks.json in your project directory, and start VS Code Debug. + +PS: You can add the .vscode directory to the .gitignore config. + +## launch.json + +It is automatically injected by Iceworks and is a standard VS Code debugging configuration. For more configuration, please refer to [VS Code debug document](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations). + + The default config is as follows: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", // Use 'Debugger for Chrome' preview. + "request": "launch", // Debugging will start a new Chrome instance, replace 'attach' can select the current Chrome instance, but Chrome needs to be restarted + "name": "Iceworks Debug", + "url": "http://localhost:3333", // Project debugging service address + "urlFilter": "http://localhost:3333/**/*", // Monitor all URLs of the project debugging service + "webRoot": "${workspaceFolder}", + "userDataDir": "${workspaceFolder}/.vscode/chrome-debug-user-data", // Used to save Chrome user data (such as installed browser extensions) + "preLaunchTask": "Iceworks Start Background Tasks", + "postDebugTask": "Iceworks Stop Background Tasks" + } + ] +} +``` + +## tasks.json + +It is automatically injected by Iceworks and is a standard VS Code debugging configuration. For more configuration, please refer to [VS Code tasks document]( https://code.visualstudio.com/docs/editor/tasks#vscode). + + The default config is as follows: + +```json +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Iceworks Start Background Tasks", + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "pattern": { + "regexp": "ERROR in .*" // build-scirpt log + }, + "background": { + "beginsPattern": ".*(@alib/build-scripts|ice\\.js|rax\\.js).*", // build-scirpt log + "endsPattern": ".*http://localhost:3333.*" // build-scirpt log + } + } + }, + { + "label": "Iceworks Stop Background Tasks", + "type": "process", + "command": [ + "${command:workbench.action.tasks.terminate}", // VS Code built-in command + "${command:workbench.action.acceptSelectedQuickOpenItem}" // VS Code built-in command + ] + } + ] +} + +``` + +## Change dev url +If the project dev url is not `http://localhost:3333`, you can modify the url address configured by `url` and `urlFilter` in launch.json, and `endsPattern` in tasks.json with the new dev url. + +## Chrome + +A new instance of Chrome is opened for debugging. The first time you start Chrome without data (no user mode, no extensions), the data operated during the debugging process will be saved (history, installed extensions ...). + +Rax project,Recommended to install [Guan Extension](https://chrome.google.com/webstore/detail/guan-extension/jfalnandddhgfnmejfgjgfbfnnkhljog)。 + +If you want to use your own Chrome, you can switch the request configuration in launch.json to `attach`. You must close the current Chrome. diff --git a/extensions/iceworks-app/docs/debug.md b/extensions/iceworks-app/docs/debug.md new file mode 100644 index 000000000..c1b37262e --- /dev/null +++ b/extensions/iceworks-app/docs/debug.md @@ -0,0 +1,84 @@ +# Iceworks Debug + +简体中文 | [English](./debug.en.md) + +使用 VS Code 和 Chrome 对源码进行调试。 + +![debug-demo](https://img.alicdn.com/tfs/TB1vCixhP39YK4jSZPcXXXrUFXa-1200-695.gif) + +点击文件右上角的 debug 按钮启动调试。 会在您的工程目录中插入 .vscode/launch.json 和 .vscode/tasks.json ,并启动 VS Code Debug。 + +PS: 可将 .vscode 目录添加至 .gitignore 配置中。 + +## launch.json + +由 Iceworks 自动注入,为标准的 VS Code 调试配置。更多配置可参考 [VS Code debug 文档](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations)。默认配置如下: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", // 通过 'Debugger for Chrome' 扩展启动 Chrome 预览。 + "request": "launch", // 调试会启动新的 Chrome 实例,替换 attach 可选取当前 Chrome 实例,但需要重启 Chrome。 + "name": "Iceworks Debug", + "url": "http://localhost:3333", // 当前工程调试服务地址 + "urlFilter": "http://localhost:3333/**/*", // 监听当前工程调试服务所有的 url + "webRoot": "${workspaceFolder}", + "userDataDir": "${workspaceFolder}/.vscode/chrome-debug-user-data", // 用于存放 Chrome 的用户数据(安装的浏览器插件等) + "preLaunchTask": "Iceworks Start Background Tasks", + "postDebugTask": "Iceworks Stop Background Tasks" + } + ] +} +``` + +## tasks.json + +由 Iceworks 自动注入,为标准的 VS Code 调试配置。更多配置可参考 [VS Code tasks 文档]( https://code.visualstudio.com/docs/editor/tasks#vscode)。默认配置如下: + +```json +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Iceworks Start Background Tasks", + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "pattern": { + "regexp": "ERROR in .*" // build-scirpt 日志 + }, + "background": { + "beginsPattern": ".*(@alib/build-scripts|ice\\.js|rax\\.js).*", // build-scirpt 日志 + "endsPattern": ".*http://localhost:3333.*" // build-scirpt 日志 + } + } + }, + { + "label": "Iceworks Stop Background Tasks", + "type": "process", + "command": [ + "${command:workbench.action.tasks.terminate}", // VS Code 内置命令 + "${command:workbench.action.acceptSelectedQuickOpenItem}" // VS Code 内置命令 + ] + } + ] +} + +``` + +## 更改端口号 +若需要 debug 的工程调试链接不为 `http://localhost:3333` 时,可以修改 launch.json 中的 `url` 和 `urlFilter` 配置的地址链接,以及 tasks.json 中的 `endsPattern` 为新的调试地址即可。 + +## Chrome 实例 + +默认开启新的 Chrome 实例进行调试。首次启动为无数据 Chrome (无用户态,无插件),在调试过程操作的数据将得以保存(历史,安装插件等)。 + +Rax 项目,推荐安装 [Guan Extension](https://chrome.google.com/webstore/detail/guan-extension/jfalnandddhgfnmejfgjgfbfnnkhljog)。 + +### 其他 + +如果想使用常用登录态的 Chrome 进行提示,可切换 launch.json 中的 request 配置为 `attach`,且必须关闭当前 Chrome。 + diff --git a/extensions/iceworks-app/package.json b/extensions/iceworks-app/package.json index cf3666f88..43977335a 100644 --- a/extensions/iceworks-app/package.json +++ b/extensions/iceworks-app/package.json @@ -3,7 +3,7 @@ "displayName": "Iceworks Application Viewer", "description": "Quick view your Universal Application(React/Rax/Vue, etc).", "publisher": "iceworks-team", - "version": "0.4.3", + "version": "0.4.4", "engines": { "vscode": "^1.41.0" }, @@ -125,8 +125,8 @@ } }, { - "command": "iceworksApp.editorMenu.runDev", - "title": "%iceworksApp.command.npmScripts.runDev.title%", + "command": "iceworksApp.editorMenu.runDebug", + "title": "%iceworksApp.command.npmScripts.runDebug.title%", "icon": { "light": "assets/light/runDev.svg", "dark": "assets/dark/runDev.svg" @@ -270,7 +270,7 @@ ], "editor/title": [ { - "command": "iceworksApp.editorMenu.runDev", + "command": "iceworksApp.editorMenu.runDebug", "group": "navigation@01", "when": "iceworks:showScriptIconInEditorTitleMenu" }, @@ -425,6 +425,8 @@ "@iceworks/project-service": "^0.1.6", "@iceworks/recorder": "^0.1.0", "@iceworks/vscode-webview": "^0.1.2", + "comment-json": "^3.0.2", + "ejs": "^3.1.3", "fs-extra": "^9.0.0", "ice-npm-utils": "^2.0.0", "latest-version": "^5.1.0", @@ -432,6 +434,7 @@ "util": "^0.12.3" }, "extensionDependencies": [ + "msjsdiag.debugger-for-chrome", "iceworks-team.iceworks-project-creator", "iceworks-team.iceworks-ui-builder", "iceworks-team.iceworks-material-helper" diff --git a/extensions/iceworks-app/package.nls.json b/extensions/iceworks-app/package.nls.json index 331a9ce11..123740975 100644 --- a/extensions/iceworks-app/package.nls.json +++ b/extensions/iceworks-app/package.nls.json @@ -13,7 +13,7 @@ "iceworksApp.command.npmScripts.refresh.title": "Refresh", "iceworksApp.command.npmScripts.run.title": "Run Script", "iceworksApp.command.npmScripts.stop.title": "Stop Script", - "iceworksApp.command.npmScripts.runDev.title": "Iceworks: Run Dev", + "iceworksApp.command.npmScripts.runDebug.title": "Iceworks: Debug", "iceworksApp.command.npmScripts.stopDev.title": "Iceworks: Stop Dev", "iceworksApp.command.npmScripts.runBuild.title": "Iceworks: Run Build", "iceworksApp.command.pages.add.title": "Add Pages", diff --git a/extensions/iceworks-app/package.nls.zh-cn.json b/extensions/iceworks-app/package.nls.zh-cn.json index bf98cb0cd..dc812a896 100644 --- a/extensions/iceworks-app/package.nls.zh-cn.json +++ b/extensions/iceworks-app/package.nls.zh-cn.json @@ -13,7 +13,7 @@ "iceworksApp.command.npmScripts.refresh.title": "刷新", "iceworksApp.command.npmScripts.run.title": "运行脚本", "iceworksApp.command.npmScripts.stop.title": "停止脚本", - "iceworksApp.command.npmScripts.runDev.title": "Iceworks: 开始本地调试", + "iceworksApp.command.npmScripts.runDebug.title": "Iceworks: 开始本地调试", "iceworksApp.command.npmScripts.stopDev.title": "Iceworks: 停止本地调试", "iceworksApp.command.npmScripts.runBuild.title": "Iceworks: 本地构建", "iceworksApp.command.pages.add.title": "添加页面", diff --git a/extensions/iceworks-app/src/constants.ts b/extensions/iceworks-app/src/constants.ts index 2cc9b5412..96fd9af68 100644 --- a/extensions/iceworks-app/src/constants.ts +++ b/extensions/iceworks-app/src/constants.ts @@ -6,5 +6,5 @@ export const nodeDepTypes: NodeDepTypes[] = ['dependencies', 'devDependencies']; export const showExtensionsQuickPickCommandId = 'iceworksApp.showExtensionsQuickPick'; -export const editorTitleRunDevCommandId = 'npmScripts-editor-title-run-dev'; +export const editorTitleRunDebugCommandId = 'npmScripts-editor-title-run-dev'; export const editorTitleRunBuildCommandId = 'npmScripts-editor-title-run-build'; diff --git a/extensions/iceworks-app/src/createEditorMenuAction.ts b/extensions/iceworks-app/src/createEditorMenuAction.ts index 6e6f1173b..4460f960b 100644 --- a/extensions/iceworks-app/src/createEditorMenuAction.ts +++ b/extensions/iceworks-app/src/createEditorMenuAction.ts @@ -1,27 +1,39 @@ import * as vscode from 'vscode'; import { createNpmCommand, checkPathExists, checkIsAliInternal, registerCommand } from '@iceworks/common-service'; import { dependencyDir, projectPath } from '@iceworks/project-service'; -import { editorTitleRunDevCommandId, editorTitleRunBuildCommandId } from './constants'; +import { setDebugConfig } from './debugConfig/index'; +import { editorTitleRunDebugCommandId, editorTitleRunBuildCommandId } from './constants'; import { ITerminalMap } from './types'; import showDefPublishEnvQuickPick from './quickPicks/showDefPublishEnvQuickPick'; import executeCommand from './commands/executeCommand'; export default async function createEditorMenuAction(terminals: ITerminalMap) { - const EDITOR_MENU_RUN_DEV = 'iceworksApp.editorMenu.runDev'; - registerCommand(EDITOR_MENU_RUN_DEV, async () => { - const pathExists = await checkPathExists(projectPath, dependencyDir); - const command: vscode.Command = { - command: EDITOR_MENU_RUN_DEV, - title: 'Run Dev', - arguments: [projectPath, createNpmCommand('run', 'start')], - }; - const commandId = editorTitleRunDevCommandId; - if (!pathExists) { - command.arguments = [projectPath, `${createNpmCommand('install')} && ${command.arguments![1]}`]; - executeCommand(terminals, command, commandId); + const EDITOR_MENU_RUN_DEBUG = 'iceworksApp.editorMenu.runDebug'; + registerCommand(EDITOR_MENU_RUN_DEBUG, async () => { + // Check dependences + if (!(await checkPathExists(projectPath, dependencyDir))) { + vscode.window.showInformationMessage('"node_modules" directory not found! Install dependencies first.'); + executeCommand( + terminals, + { + command: EDITOR_MENU_RUN_DEBUG, + title: 'Run Install', + arguments: [projectPath, createNpmCommand('install')], + }, + editorTitleRunDebugCommandId + ); return; } - executeCommand(terminals, command, commandId); + + // Prepare VS Code debug config + setDebugConfig(); + + // Run Debug + let workspaceFolder; + if (vscode.workspace.workspaceFolders) { + workspaceFolder = vscode.workspace.workspaceFolders[0]; + } + vscode.debug.startDebugging(workspaceFolder, 'Iceworks Debug'); }); const isAliInternal = await checkIsAliInternal(); diff --git a/extensions/iceworks-app/src/debugConfig/getDefaultConfigs.ts b/extensions/iceworks-app/src/debugConfig/getDefaultConfigs.ts new file mode 100644 index 000000000..e567f8a57 --- /dev/null +++ b/extensions/iceworks-app/src/debugConfig/getDefaultConfigs.ts @@ -0,0 +1,77 @@ +import * as ejs from 'ejs'; +import { CONFIG_NAME, START_URL, CONFIG_START_LABEL, CONFIG_STOP_LABEL, IDebugConfig } from './index'; + +const launchConfigTemplate = ` +[ + { + "type": "chrome", + "request": "launch", + "name": "<%= configName %>", + "url": "<%= startUrl %>", + "urlFilter": "<%= startUrl %>/**/*", + "webRoot": "\${workspaceFolder}", + "userDataDir": "\${workspaceFolder}/.vscode/chrome-debug-user-data", + "preLaunchTask": "<%= startLabel %>", + "postDebugTask": "<%= stopLabel %>" + } +] +`; + +const tasksConfigTemplate = ` +[ + { + "label": "<%= startLabel %>", + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "pattern": { + "regexp": "ERROR in .*" + }, + "background": { + "beginsPattern": ".*(@alib/build-scripts|ice\\\\.js|rax\\\\.js).*", + "endsPattern": ".*<%= startUrl %>.*" + } + } + }, + { + "label": "<%= stopLabel %>", + "type": "process", + "command":[ + "\${command:workbench.action.tasks.terminate}", + "\${command:workbench.action.acceptSelectedQuickOpenItem}" + ] + } +] +`; + +// https://code.visualstudio.com/docs/editor/debugging#_launch-configurations +export function getLaunchConfig(): IDebugConfig { + const DEBUG_LAUNCH_VERSION = '0.2.0'; + return { + version: DEBUG_LAUNCH_VERSION, + configurations: JSON.parse( + ejs.render(launchConfigTemplate, { + configName: CONFIG_NAME, + startUrl: START_URL, + startLabel: CONFIG_START_LABEL, + stopLabel: CONFIG_STOP_LABEL, + }) + ), + }; +} + +// https://code.visualstudio.com/docs/editor/tasks#vscode +export function getTasksConfig(): IDebugConfig { + const DEBUG_TASKS_VERSION = '2.0.0'; + return { + version: DEBUG_TASKS_VERSION, + tasks: JSON.parse( + ejs.render(tasksConfigTemplate, { + startUrl: START_URL, + startLabel: CONFIG_START_LABEL, + stopLabel: CONFIG_STOP_LABEL, + }) + ), + }; +} diff --git a/extensions/iceworks-app/src/debugConfig/index.ts b/extensions/iceworks-app/src/debugConfig/index.ts new file mode 100644 index 000000000..03df57e99 --- /dev/null +++ b/extensions/iceworks-app/src/debugConfig/index.ts @@ -0,0 +1,74 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import { parse } from 'comment-json'; +import { getLaunchConfig, getTasksConfig } from './getDefaultConfigs'; + +// Iceworks debug config +export const CONFIG_NAME = 'Iceworks Debug'; +export const CONFIG_START_LABEL = 'Iceworks Start Background Tasks'; +export const CONFIG_STOP_LABEL = 'Iceworks Stop Background Tasks'; +export const START_URL = 'http://localhost:3333'; + +export interface IDebugConfig { + version?: string; + tasks?: any[]; + configurations?: any[]; +} + +function writeConfigFile(filePath: string, config: IDebugConfig) { + fs.writeFileSync( + filePath, + '// See https://github.com/ice-lab/iceworks/blob/master/extensions/iceworks-app/docs/debug.md \n' + + '// for the documentation about the Iceworks debug \n' + + `${JSON.stringify(config, null, ' ')}` + ); +} + +// Prepare VS Code debug config +export function setDebugConfig() { + const { rootPath = __dirname } = vscode.workspace; + + // Make .vscode directory + const targetDir = path.join(rootPath, '.vscode'); + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir); + } + + // Set launch.json + let launchConfig; + const defaultLaunchConfig = getLaunchConfig(); + const launchConfigPath = path.join(targetDir, 'launch.json'); + if (fs.existsSync(launchConfigPath)) { + const configurations: any[] = []; + launchConfig = parse(fs.readFileSync(launchConfigPath).toString()); + (launchConfig.configurations || []).forEach((configuration) => { + if (configuration.name !== CONFIG_NAME) { + configurations.push(configuration); + } + }); + launchConfig.configurations = configurations.concat(defaultLaunchConfig.configurations); + } else { + launchConfig = defaultLaunchConfig; + } + writeConfigFile(launchConfigPath, launchConfig); + + // Set tasks.json + let tasksConfig; + const defaultTasksConfig = getTasksConfig(); + const tasksConfigPath = path.join(targetDir, 'tasks.json'); + if (fs.existsSync(tasksConfigPath)) { + const tasks: any[] = []; + + tasksConfig = parse(fs.readFileSync(tasksConfigPath).toString()); + (tasksConfig.tasks || []).forEach((task) => { + if (task.label !== CONFIG_START_LABEL && task.label !== CONFIG_STOP_LABEL) { + tasks.push(task); + } + }); + tasksConfig.tasks = tasks.concat(defaultTasksConfig.tasks); + } else { + tasksConfig = defaultTasksConfig; + } + writeConfigFile(tasksConfigPath, tasksConfig); +} diff --git a/extensions/iceworks-config-helper/.vscode/launch.json b/extensions/iceworks-config-helper/.vscode/launch.json index 306a0e1aa..e5ea897b0 100644 --- a/extensions/iceworks-config-helper/.vscode/launch.json +++ b/extensions/iceworks-config-helper/.vscode/launch.json @@ -1,4 +1,4 @@ -// A launch configuration that launches the extension inside a new window +// A launch configuration that compiles the extension and then opens it inside a new window // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 @@ -6,25 +6,13 @@ "version": "0.2.0", "configurations": [ { - "name": "Run Extension", + "name": "Launch Extension", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": ["--extensionDevelopmentPath=${workspaceFolder}"], - "outFiles": ["${workspaceFolder}/out/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" - }, - { - "name": "Extension Tests", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/test/suite/index" - ], - "outFiles": ["${workspaceFolder}/out/test/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" + "outFiles": ["${workspaceFolder}/build/*.js"], + "preLaunchTask": "npm: webpack" } ] } diff --git a/extensions/iceworks-config-helper/.vscodeignore b/extensions/iceworks-config-helper/.vscodeignore index f16fa5501..22e8d358c 100644 --- a/extensions/iceworks-config-helper/.vscodeignore +++ b/extensions/iceworks-config-helper/.vscodeignore @@ -1 +1,10 @@ .vscode/** +.vscode-test/** +src/** +.gitignore +**/tsconfig.json +**/*.map +**/*.ts +node_modules +webpack.config.js +web/** \ No newline at end of file diff --git a/extensions/iceworks-config-helper/CHANGELOG.md b/extensions/iceworks-config-helper/CHANGELOG.md index 3f377f803..d01ab5953 100644 --- a/extensions/iceworks-config-helper/CHANGELOG.md +++ b/extensions/iceworks-config-helper/CHANGELOG.md @@ -1,13 +1,20 @@ # Change Log +## 0.3.0 + +- feat: add visual editing panel. +- feat: add rax app.json validation. +- feat: update browserslist. +- fix: update ice & rax validation. + ## 0.2.1 - feat: update rax PHA plugin config. ## 0.2.0 -- feat: add ICE APP build.json validation +- feat: add ICE APP build.json validation. ## 0.1.0 -- feat: add build.json validation +- feat: add build.json validation. diff --git a/extensions/iceworks-config-helper/README.en.md b/extensions/iceworks-config-helper/README.en.md index 9743ade83..4ae438f1f 100644 --- a/extensions/iceworks-config-helper/README.en.md +++ b/extensions/iceworks-config-helper/README.en.md @@ -9,19 +9,44 @@ For icejs and rax-app configuration files(like app.json/build.json), provides vi ## Usage -### Hover Widget +### Editing Config File Visually -Whenever you hover over some JSON key, shows the description in a Hover widget. +Visually edit the configuration file through the palette. + +### Activite + +#### Active In Side Bar + +![demo](https://user-images.githubusercontent.com/56879942/89491695-68db3a80-d7e2-11ea-8bfe-af1b545eb2ad.gif) + +1. Right click on the configuration file in the sidebar and select `Iceworks: Setup xxx.json by visually.` to launch the visual editing panel + +#### Active In TextEditer + +![demo](https://user-images.githubusercontent.com/56879942/89491449-e94d6b80-d7e1-11ea-8780-051ff9845023.gif) + +1. Open the configuration file, right click `Iceworks: Setup xxx.json by visually.` to launch the visual editing panel +2. Click the `Iceworks: Setup xxx.json by visually.` icon in the title bar and click it to launch the visual editing panel + +### Demo + +![demo](https://user-images.githubusercontent.com/56879942/89491159-30872c80-d7e1-11ea-9463-501248e1b08c.gif) + +### IntelliSense + +#### Hover Widget + +Whenever you hover over some Json key, shows the description in a Hover widget. ![demo](https://user-images.githubusercontent.com/56879942/87398212-290ca300-c5e8-11ea-9596-c15c380c0d7c.gif) -### Code Completion +#### Code Completion When you input it will show key suggestions related to your config. ![demo](https://user-images.githubusercontent.com/56879942/87398228-2e69ed80-c5e8-11ea-8b2e-611924fa76bb.gif) -### Snippets +#### Snippets Use code snippets for quick configuration: diff --git a/extensions/iceworks-config-helper/README.md b/extensions/iceworks-config-helper/README.md index 13cbc05dc..a28b0c3bf 100644 --- a/extensions/iceworks-config-helper/README.md +++ b/extensions/iceworks-config-helper/README.md @@ -9,19 +9,51 @@ ## 功能 -### 悬停提示 +### 可视化表单设置 -当您将鼠标悬停在某个 JSON 的字段上时,会出现悬浮小部件显示字段的描述。 +通过可视化表单设置的方式对配置文件进行编辑。 + +### 激活 + +#### 在侧边栏激活面板 + +![使用说明](https://user-images.githubusercontent.com/56879942/89489442-18ada980-d7dd-11ea-8db3-1751e5db2eca.gif) + +1. 在资源文件夹选择配置文件,点击鼠标右键 +2. 在右键列表中选择 `Iceworks: 可视化设置 xxx.json` +3. 启动可视化配置面板 + +#### 在文档编辑器中激活面板 + +![使用说明](https://user-images.githubusercontent.com/56879942/89489443-19464000-d7dd-11ea-9eeb-2958027525c4.gif) + +1. 打开配置文件,点击鼠标右键 +2. 在右键列表中选择 `Iceworks: 可视化设置 xxx.json` +3. 启动可视化配置面板 + +或者: + +点击标签栏右侧的搜索图标,启动可视化配置面板。 + +### 演示 + +![使用说明](https://user-images.githubusercontent.com/56879942/89489431-12b7c880-d7dd-11ea-9802-8f3fa51304f0.gif) + +### 代码编辑辅助 + +#### 悬停提示 + +当您将鼠标悬停在某个 Json 的字段上时,会出现悬浮小部件显示字段的描述。 ![使用说明](https://user-images.githubusercontent.com/56879942/87398212-290ca300-c5e8-11ea-9596-c15c380c0d7c.gif) -### 自动补全 +#### 自动补全 当您在输入时,显示与您的配置相关的建议。 ![使用说明](https://user-images.githubusercontent.com/56879942/87398228-2e69ed80-c5e8-11ea-8b2e-611924fa76bb.gif) -### 代码片段 +#### 代码片段 预设标准的代码片段,方便您进行快速配置。 diff --git a/extensions/iceworks-config-helper/icons/preview-right-dark.svg b/extensions/iceworks-config-helper/icons/preview-right-dark.svg new file mode 100644 index 000000000..efb8c1cd1 --- /dev/null +++ b/extensions/iceworks-config-helper/icons/preview-right-dark.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/extensions/iceworks-config-helper/icons/preview-right-light.svg b/extensions/iceworks-config-helper/icons/preview-right-light.svg new file mode 100644 index 000000000..d1cd95f29 --- /dev/null +++ b/extensions/iceworks-config-helper/icons/preview-right-light.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/extensions/iceworks-config-helper/package.json b/extensions/iceworks-config-helper/package.json index b956c0002..60130f817 100644 --- a/extensions/iceworks-config-helper/package.json +++ b/extensions/iceworks-config-helper/package.json @@ -1,34 +1,98 @@ { "name": "iceworks-config-helper", "displayName": "Iceworks Config Helper", - "description": "Easily write Config files in icejs && raxapp", + "description": "Easily write Config files in icejs(& rax-app)", "publisher": "iceworks-team", - "version": "0.2.1", + "version": "0.3.0", "engines": { "vscode": "^1.41.0" }, "categories": [ - "Other" + "Other", + "Programming Languages" ], "keywords": [ "ICE", "Rax", "config", - "raxjs", - "icejs" + "json", + "icejs", + "rax-app" ], "icon": "assets/logo.png", "activationEvents": [ - "*" + "iceworks-config-helper.buildJson.start", + "iceworks-config-helper.appJson.start", + "onLanguage:json" ], - "main": "./build/index.js", + "main": "./build/extension.js", "contributes": { "jsonValidation": [ { "fileMatch": "build.json", - "url": "./schemas/ice.build.json" + "url": "./schemas/ice.build.en.json" + }, + { + "fileMatch": "app.json", + "url": "./schemas/rax.app.en.json" + } + ], + "commands": [ + { + "command": "iceworks-config-helper.buildJson.start", + "title": "%iceworksConfigHelper.commands.start.buildJson.title%", + "icon": { + "light": "./icons/preview-right-light.svg", + "dark": "./icons/preview-right-dark.svg" + } + }, + { + "command": "iceworks-config-helper.appJson.start", + "title": "%iceworksConfigHelper.commands.start.appJson.title%", + "icon": { + "light": "./icons/preview-right-light.svg", + "dark": "./icons/preview-right-dark.svg" + } } - ] + ], + "menus": { + "editor/context": [ + { + "command": "iceworks-config-helper.buildJson.start", + "when": "iceworks:showWebViewPanelForBuildJson", + "group": "configHelper" + }, + { + "command": "iceworks-config-helper.appJson.start", + "when": "iceworks:showWebViewPanelForAppJson", + "group": "configHelper" + } + ], + "editor/title": [ + { + "command": "iceworks-config-helper.buildJson.start", + "when": "iceworks:showWebViewPanelForBuildJson", + "group": "navigation" + }, + { + "command": "iceworks-config-helper.appJson.start", + "when": "iceworks:showWebViewPanelForAppJson", + "group": "navigation" + } + ], + "explorer/context": [ + { + "command": "iceworks-config-helper.buildJson.start", + "when": "resourceFilename == build.json", + "group": "2_workspace" + }, + { + "command": "iceworks-config-helper.appJson.start", + "when": "resourceFilename == app.json", + "group": "2_workspace" + } + ] + } }, "repository": { "type": "git", @@ -40,16 +104,28 @@ "email": "iceworksteam@163.com" }, "scripts": { - "vscode:prepublish": "rm -rf build && npm run compile", - "compile": "tsc -p ./", - "watch": "tsc -watch -p ./tsconfig.json" + "vscode:prepublish": "rm -rf build && npm run build:web && webpack --mode production", + "build:web": "cd web && npm run build", + "watch:web": "cd web && npm run start", + "webpack": "webpack --mode development", + "webpack-dev": "webpack --mode development --watch", + "compile": "tsc -p ./tsconfig.json" }, "devDependencies": { + "@types/lodash": "^4.14.158", "@types/node": "^12.11.7", "@types/vscode": "^1.41.0", - "typescript": "^3.6.4" + "ts-loader": "^7.0.5", + "typescript": "^3.8.3", + "webpack": "^4.43.0", + "webpack-cli": "^3.3.12" }, "dependencies": { - "@iceworks/project-service": "^0.1.6" + "@iceworks/common-service": "^0.1.14", + "@iceworks/i18n": "^0.1.0", + "@iceworks/project-service": "^0.1.15", + "@iceworks/vscode-webview": "^0.1.5", + "lodash.forin": "^4.4.0", + "lodash.isequal": "^4.5.0" } } diff --git a/extensions/iceworks-config-helper/package.nls.json b/extensions/iceworks-config-helper/package.nls.json new file mode 100644 index 000000000..3c3e28e9c --- /dev/null +++ b/extensions/iceworks-config-helper/package.nls.json @@ -0,0 +1,4 @@ +{ + "iceworksConfigHelper.commands.start.buildJson.title": "Iceworks: Setup build.json by visually.", + "iceworksConfigHelper.commands.start.appJson.title": "Iceworks: Setup app.json by visually." +} diff --git a/extensions/iceworks-config-helper/package.nls.zh-cn.json b/extensions/iceworks-config-helper/package.nls.zh-cn.json new file mode 100644 index 000000000..b9793e291 --- /dev/null +++ b/extensions/iceworks-config-helper/package.nls.zh-cn.json @@ -0,0 +1,4 @@ +{ + "iceworksConfigHelper.commands.start.buildJson.title": "Iceworks: 可视化设置 build.json", + "iceworksConfigHelper.commands.start.appJson.title": "Iceworks: 可视化设置 app.json." +} diff --git a/extensions/iceworks-config-helper/schemas/ice.build.json b/extensions/iceworks-config-helper/schemas/ice.build.en.json similarity index 59% rename from extensions/iceworks-config-helper/schemas/ice.build.json rename to extensions/iceworks-config-helper/schemas/ice.build.en.json index 6331d7472..bb2b5aee0 100644 --- a/extensions/iceworks-config-helper/schemas/ice.build.json +++ b/extensions/iceworks-config-helper/schemas/ice.build.en.json @@ -1,8 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema", "type": "object", - "title": "The ICE APP build.json Schema", - "description": "The root schema comprises the entire JSON document.", + "title": "icejs build.json", + "description": "Engineering configuration for icejs.", "default": {}, "examples": [{}], "required": [], @@ -43,21 +43,44 @@ "additionalProperties": true }, "hash": { - "type": ["boolean", "string"], - "description": "If you want the hashed version of the built resource, you can set hash to true.", + "oneOf": [ + { + "type": "string", + "description": "If you want the hashed version of the built resource, you can set hash to true.", + "examples": ["contenthash"] + }, + { + "type": "boolean", + + "default": false, + "examples": [false] + } + ], + "description": "If you want the hashed version of the built resource, you can set hash to true. you can also switch hash type by string ", "default": false, - "examples": ["contenthash"] + "properties": {} }, "injectBabel": { - "type": ["boolean", "string"], + "oneOf": [ + { + "type": "string", + "description": "Whether to inject core-js/stable and regenerator-runtime/runtime." + }, + { + "type": "boolean", + + "default": false, + "examples": [false] + } + ], "description": "Whether to inject core-js/stable and regenerator-runtime/runtime.", "default": false, - "examples": [false] + "properties": {} }, "minify": { "type": "boolean", "description": "The resources after construction will be compressed.", - "default": false, + "default": true, "examples": [false] }, "outputAssetsPath": { @@ -150,7 +173,7 @@ "vendor": { "type": "boolean", "description": "Configure whether to generate vendor.", - "default": false, + "default": true, "examples": [true] }, "libraryTarget": { @@ -173,7 +196,7 @@ }, "compileDependencies": { "type": "array", - "description": "y default, babel-loader will compile relevant modules to be compatible with IE11. If you need babel to compile the specified file under node_modules, you can quickly add it in this configuration.", + "description": "By default, babel-loader will compile relevant modules to be compatible with IE11. If you need babel to compile the specified file under node_modules, you can quickly add it in this configuration.", "default": [], "examples": [["@alifd/next"]], "additionalItems": true, @@ -235,20 +258,20 @@ "description": "Add an additional babel plugin to the babel-loader configuration.", "default": [], "examples": [[]], - "additionalItems": true, "items": { - "anyOf": [] - } + "type": "object" + }, + "additionalItems": true }, "babelPresets": { "type": "array", "description": "Add an additional babel preset to the configuration of babel-loader. If the preset is the same as the built-in preset, the configuration content in babelPresets will be used first.", "default": [], "examples": [[]], - "additionalItems": true, "items": { - "anyOf": [] - } + "type": "object" + }, + "additionalItems": true }, "ignoreHtmlTemplate": { "type": "boolean", @@ -257,16 +280,26 @@ "examples": [false] }, "eslint": { - "type": ["object", "boolean"], - "description": "The eslint code detection is disabled by default. If you need to enable it, set it to true. Configure eslint related options see https://github.com/webpack-contrib/eslint-loader .", - "default": {}, - "examples": [{}], - "required": [], - "additionalProperties": true, + "oneOf": [ + { + "type": "boolean", + "description": "The eslint code detection is disabled by default. If you need to enable it, set it to true." + }, + { + "type": "object", + "additionalProperties": true + } + ], + "description": "Configure eslint related options see https://github.com/webpack-contrib/eslint-loader .", + "default": false, + "examples": [false], "properties": {} }, "targets": { - "type": "object", + "type": ["object", "array"] + }, + "browserslist": { + "type": ["object", "array", "string"], "description": "Configure the targets of @babel/preset-env, configure the minimum version of the browser, the newly configured targets will override the default values.", "default": {}, "examples": [ @@ -356,6 +389,123 @@ ] } }, + { + "type": "array", + "description": "See https://ice.work/docs/guide/advance/antd .", + "default": [], + "examples": [ + [ + "build-plugin-antd", + { + "themeConfig": { + "primary-color": "#1DA57A" + } + } + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "See https://ice.work/docs/guide/advance/fusion .", + "default": "", + "examples": ["build-plugin-antd"] + }, + { + "type": "object", + "description": "build-plugin-antd config.", + "default": {}, + "examples": [ + { + "themeConfig": { + "primary-color": "#1DA57A" + } + } + ], + "additionalProperties": true, + "properties": { + "themeConfig": { + "type": "string", + "description": "customize themeConfig", + "default": "", + "examples": [ + { + "primary-color": "#1DA57A" + } + ] + }, + "importOptions": { + "type": "object", + "description": "The same as babel-plugin-import config", + "properties": { + "libraryName": { + "type": "string" + }, + "libraryDirectory": { + "type": "string" + }, + "style": { + "type": "boolean" + } + } + } + } + } + ] + } + }, + { + "type": "array", + "description": "Provides the ability to localize network resources in CSS.", + "default": [], + "examples": [ + [ + [ + "build-plugin-css-assets-local", + { + "outputPath": "assets", + "relativeCssPath": "../" + } + ] + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "Provides the ability to localize network resources in CSS..", + "default": "", + "examples": ["build-plugin-css-assets-local"] + }, + { + "type": "object", + "description": "build-plugin-css-assets-local config.", + "default": {}, + "examples": [ + { + "outputPath": "assets", + "relativeCssPath": "../" + } + ], + "additionalProperties": true, + "properties": { + "outputPath": { + "type": "string", + "description": "Prefix of extracted file directory", + "default": "" + }, + "relativeCssPath": { + "type": "string", + "description": "The path of the extracted file relative to the CSS", + "default": "" + } + } + } + ] + } + }, { "type": "array", "description": "See https://ice.work/docs/guide/develop/plugin-list .", @@ -410,11 +560,76 @@ ] } }, + { + "type": "array", + "description": "Provide the ability to automatically load assets.", + "default": [], + "examples": [ + [ + [ + "build-plugin-load-assets", + { + "assets": ["https://unpkg.com/lodash@4.17.11/index.js", "https://url/global.css"] + } + ] + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "Provide the ability to automatically load assets.", + "default": "", + "examples": ["plugin-load-assets"] + }, + { + "type": "object", + "description": "plugin-load-assets config.", + "default": {}, + "examples": [ + { + "assets": ["https://unpkg.com/lodash@4.17.11/index.js", "https://url/global.css"] + } + ], + "additionalProperties": true, + "properties": { + "assets": { + "type": "array", + "description": "Load assets from url", + "default": "", + "items": { + "type": "string" + } + } + } + } + ] + } + }, { "type": "string", - "description": "See https://ice.work/docs/guide/develop/plugin-list .", + "description": "Used for debug, commonly seen in the online environment loading local assets for debugging scenarios:", + "default": "", + "examples": ["build-plugin-smart-debug"] + }, + { + "type": "string", + "description": "The plug-in supports a JSX extension syntax called JSX+, which helps business developers write JSX more easily and faster.", + "default": "", + "examples": ["build-plugin-jsx-plus"] + }, + { + "type": "string", + "description": "This plugin is an integration of React - Refresh, a function similar to hot reloading, but with a more powerful fast hot update mechanism that allows you to get near-instant (1-2 seconds) feedback on changes made to react components without losing state when changes are made to react components during hot updates.", + "default": "", + "examples": ["build-plugin-fast-refresh"] + }, + { + "type": "string", + "description": "For icEJS compatibility handling with the capabilities of using WebPack5, which has not yet been officially released, use this plug-in with caution", "default": "", - "examples": ["@ali/build-plugin-ice-def"] + "examples": ["build-plugin-webpack5"] } ] } diff --git a/extensions/iceworks-config-helper/schemas/ice.build.zh-cn.json b/extensions/iceworks-config-helper/schemas/ice.build.zh-cn.json new file mode 100644 index 000000000..edd7d75cb --- /dev/null +++ b/extensions/iceworks-config-helper/schemas/ice.build.zh-cn.json @@ -0,0 +1,625 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "title": "icejs build.json", + "description": "icejs 的工程配置", + "default": {}, + "examples": [{}], + "required": [], + "properties": { + "alias": { + "type": "object", + "description": "使用别名简化引入语句。在 icejs 默认配置了 { \"@\": \"./src/\" }。", + "default": {}, + "examples": [ + { + "@components": "src/components/" + } + ] + }, + "publicPath": { + "type": "string", + "description": "配置 webpack 的 output.publicPath 属性。 仅在运行 build 命令时生效。", + "default": "", + "examples": ["/"] + }, + "devPublicPath": { + "type": "string", + "description": "配置 webpack 的 output.publicPath 属性。 仅在运行 start 命令时生效。", + "default": "", + "examples": ["/"] + }, + "sourceMap": { + "type": "boolean", + "description": "是否生成 sourceMap", + "default": false, + "examples": [false] + }, + "externals": { + "type": "object", + "description": "将某些 import 的包排除在 bundle 之外,在运行时再去外部获取这些依赖。 比如,从 CDN 引入 React 资源,而不是将它打包, 详细配置同 webpack 的 extenerals 请参考 https://webpack.js.org/configuration/externals .", + "default": {}, + "examples": [{}], + "additionalProperties": true + }, + "hash": { + "oneOf": [ + { + "type": "string", + "examples": ["contenthash"] + }, + { + "type": "boolean", + "default": false, + "examples": [false] + } + ], + "description": "如果希望构建后的资源带 hash 版本,可以将 hash 设置为 true,也可以通过设置为 contenthash 的方式:\"hash\": \"contenthash\"", + "default": false, + "properties": {} + }, + "injectBabel": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "boolean", + + "default": false, + "examples": [false] + } + ], + "description": "默认情况下会注入 core-js/stable 和 regenerator-runtime/runtime,根据 targets 配置的兼容浏览器进行 polyfill,实现按需添加。 开发类库项目,可以将配置设置为 runtime。 如果想手动 polyfill,可以将配置设置为 false,工程将不会进行自动的 polyfill。", + "default": false, + "properties": {} + }, + "minify": { + "type": "boolean", + "description": "构建后的资源将进行压缩,如果不希望资源被压缩可以修改为 false", + "default": true, + "examples": [false] + }, + "outputAssetsPath": { + "type": "object", + "description": "修改构建后的 css/js 文件目录,默认情况下 css 在 build/css/ 下,js 在 build/js/ 下", + "default": {}, + "examples": [ + { + "js": "js", + "css": "css" + } + ], + "additionalProperties": true + }, + "outputDir": { + "type": "string", + "description": "修改构建后的文件目录", + "default": "", + "examples": ["build"] + }, + "proxy": { + "type": "object", + "description": "配置 webpack 的 devServer.proxy 属性。建议使用 proxy 来设置代理而不要修改 webpack 的 devServer.proxy", + "default": {}, + "examples": [ + { + "/**": { + "enable": true, + "target": "http://127.0.0.1:6001" + } + } + ], + "additionalProperties": true + }, + "devServer": { + "type": "object", + "description": " webpack-dev-server 将采用这个选项下的内容,你可以配置多项属性来进行定制注意,devServer 不支持 port 属性配置,如需改变端口,请通过命令行参数传入。", + "default": {}, + "examples": [ + { + "compress": true, + "disableHostCheck": true, + "clientLogLevel": "error", + "hot": true, + "quiet": true, + "overlay": false + } + ], + "additionalProperties": true, + "properties": { + "compress": { + "type": "boolean", + "description": "为所有服务启用 gzip 压缩", + "default": false + }, + "disableHostCheck": { + "type": "boolean", + "description": "当设置为真时,此选项绕过主机检查。我们不建议这样做,因为不检查主机的应用程序容易受到DNS重新绑定攻击。", + "default": false + }, + "clientLogLevel": { + "type": "string", + "description": "DevTools中的控制台将显示不同级别的消息。", + "default": "error", + "pattern": "^(info|silent|trace|debug|info|warn|error|none|warning)$" + }, + "hot": { + "type": "boolean", + "description": "启用 webpack 的热模块替换功能", + "default": false + }, + "quiet": { + "type": "boolean", + "description": "是否设置静音。启用静音后,除了初始启动信息外,不会向控制台写入任何内容。", + "default": false + }, + "overlay": { + "type": "boolean", + "description": "当出现编译器错误或警告时,在浏览器中显示全屏覆盖。这将只显示编译器错误。", + "default": false + } + } + }, + "targets": { + "type": "object", + "description": "配置 @babel/preset-env 的 targets,配置浏览器最低版本,新配置的 targets 会覆盖默认值。", + "default": {}, + "examples": [ + { + "chrome": 49, + "ie": 11 + } + ], + "required": [], + "additionalProperties": true + }, + "vendor": { + "type": "boolean", + "description": "配置是否生成 vendor", + "default": true, + "examples": [true] + }, + "libraryTarget": { + "type": "string", + "description": "配置 webpack 的 output.libraryTarget 属性。", + "default": "", + "examples": [""] + }, + "library": { + "type": "string", + "description": "配置 webpack 的 output.library 属性。", + "default": "", + "examples": [""] + }, + "libraryExport": { + "type": "string", + "description": "配置 webpack 的 output.libraryExport 属性。", + "default": "", + "examples": [""] + }, + "compileDependencies": { + "type": "array", + "description": "默认情况下 babel-loader 会编译相关模块以兼容 IE11。如果需要 babel 去编译 node_modules 下的指定文件,可以在这个配置快捷添加。", + "default": [], + "examples": [["@alifd/next"]], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "Compile module dependencies under node_modules.", + "default": "", + "examples": ["@alifd/next"] + } + ] + } + }, + "cssLoaderOptions": { + "type": "object", + "description": "为 css-loader 提供快捷配置,将与默认配置进行浅合并。详细配置可参考 https://webpack.js.org/loaders/css-loader/#options .", + "default": {}, + "examples": [{}], + "required": [], + "additionalProperties": true, + "properties": {} + }, + "lessLoaderOptions": { + "type": "object", + "description": "为 less-loader 提供快捷配置,将与默认配置进行浅合并。详细配置可参考 https://webpack.js.org/loaders/less-loader/#options .", + "default": {}, + "examples": [{}], + "required": [], + "additionalProperties": true, + "properties": {} + }, + "sassLoaderOptions": { + "type": "object", + "description": "为 sass-loader 提供快捷配置,将与默认配置进行浅合并。详细配置可参考 https://webpack.js.org/loaders/sass-loader/#options .", + "default": {}, + "examples": [{}], + "required": [], + "additionalProperties": true, + "properties": {} + }, + "postcssrc": { + "type": "boolean", + "description": "开启配置项后,工程上将清空内置 postcss 配置,读取 postcss 配置文件 postcssrc.js 或 postcss.config.js 中的配置。", + "default": false, + "examples": [false] + }, + "terserOptions": { + "type": "object", + "description": "为 terserPlugin 提供快捷配置,将与默认配置进行浅合并。详细配置可参考 terser options", + "default": {}, + "examples": [{}], + "required": [], + "additionalProperties": true, + "properties": {} + }, + "babelPlugins": { + "type": "array", + "description": "为 babel-loader 的配置追加额外的 babel plugin。", + "default": [], + "examples": [[]], + "items": { + "type": "object" + }, + "additionalItems": true + }, + "babelPresets": { + "type": "array", + "description": "为 babel-loader 的配置追加额外的 babel preset。如果配置 preset 与内置相同,则优先使用 babelPresets 中的配置内容。", + "default": [], + "examples": [[]], + "items": { + "type": "object" + }, + "additionalItems": true + }, + "ignoreHtmlTemplate": { + "type": "boolean", + "description": "开启后,在 build 构建时,将移除所有内置 html-webpack-plugin 设置,不再生成 html 文件。", + "default": false, + "examples": [false] + }, + "eslint": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": true + } + ], + "description": "默认关闭 eslint 代码检测,如需开启配置为 true 即可。", + "default": false, + "examples": [false], + "properties": {} + }, + "disableRuntime": { + "type": "boolean", + "description": "禁用运行时的能力,如需关闭配置为 true 即可。", + "default": false, + "examples": [false] + }, + "tsChecker": { + "type": "boolean", + "description": "默认关闭 TypeScript 类型检测,如需开启配置为 true 即可。", + "default": false, + "examples": [false] + }, + "plugins": { + "type": "array", + "description": "编译插件", + "default": [], + "examples": [ + [ + [ + "build-plugin-fusion", + { + "themePackage": "@alifd/theme-design-pro" + } + ], + [ + "build-plugin-moment-locales", + { + "locales": ["zh-cn"] + } + ] + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "array", + "description": "详见 https://ice.work/docs/guide/advance/fusion .", + "default": [], + "examples": [ + [ + "build-plugin-fusion", + { + "themePackage": "@alifd/theme-design-pro" + } + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "详见 https://ice.work/docs/guide/advance/fusion .", + "default": "", + "examples": ["build-plugin-fusion"] + }, + { + "type": "object", + "description": "build-plugin-fusion 插件设置.", + "default": {}, + "examples": [ + { + "themePackage": "@alifd/theme-design-pro" + } + ], + "additionalProperties": true, + "properties": { + "themePackage": { + "type": "string", + "description": "设置主题,例如: @alifd/theme-design-pro .", + "default": "", + "examples": ["@alifd/theme-design-pro"] + } + } + } + ] + } + }, + { + "type": "array", + "description": "详见 https://ice.work/docs/guide/advance/antd .", + "default": [], + "examples": [ + [ + "build-plugin-antd", + { + "themeConfig": { + "primary-color": "#1DA57A" + } + } + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "详见 https://ice.work/docs/guide/advance/fusion .", + "default": "", + "examples": ["build-plugin-antd"] + }, + { + "type": "object", + "description": "设置 build-plugin-antd 插件.", + "default": {}, + "examples": [ + { + "themeConfig": { + "primary-color": "#1DA57A" + } + } + ], + "additionalProperties": true, + "properties": { + "themeConfig": { + "type": "string", + "description": "自定义主题", + "default": "", + "examples": [ + { + "primary-color": "#1DA57A" + } + ] + }, + "importOptions": { + "type": "object", + "description": "importOptions 同 babel-plugin-import 按需加载配置,默认参数 { libraryDirectory: 'es', style: true},根据用户设置进行合并", + "properties": { + "libraryName": { + "type": "string" + }, + "libraryDirectory": { + "type": "string" + }, + "style": { + "type": "boolean" + } + } + } + } + } + ] + } + }, + { + "type": "array", + "description": "build-plugin-css-assets-local 提供将 css 中的网络资源本地化能力", + "default": [], + "examples": [ + [ + [ + "build-plugin-css-assets-local", + { + "outputPath": "assets", + "relativeCssPath": "../" + } + ] + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "build-plugin-css-assets-local 提供将 css 中的网络资源本地化能力", + "default": "", + "examples": ["build-plugin-css-assets-local"] + }, + { + "type": "object", + "description": "build-plugin-css-assets-local 设置", + "default": {}, + "examples": [ + { + "outputPath": "assets", + "relativeCssPath": "../" + } + ], + "additionalProperties": true, + "properties": { + "outputPath": { + "type": "string", + "description": "提取后的文件目录前缀", + "default": "" + }, + "relativeCssPath": { + "type": "string", + "description": "提取的文件后相对于 CSS 的路径", + "default": "" + } + } + } + ] + } + }, + { + "type": "array", + "description": "详见 https://ice.work/docs/guide/develop/plugin-list .", + "default": [], + "examples": [ + [ + "build-plugin-moment-locales", + { + "locales": ["zh-cn"] + } + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "详见 https://ice.work/docs/guide/develop/plugin-list .", + "default": "", + "examples": ["build-plugin-moment-locales"] + }, + { + "type": "object", + "description": "build-plugin-moment-locales config.", + "default": {}, + "examples": [ + { + "locales": ["zh-cn"] + } + ], + "additionalProperties": true, + "properties": { + "locales": { + "type": "array", + "description": "build-plugin-moment-locales 插件设置 ", + "default": [], + "examples": [["zh-cn"]], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "build-plugin-moment-locales 插件本地化设置", + "default": "", + "examples": ["zh-cn"] + } + ] + } + } + } + } + ] + } + }, + { + "type": "array", + "description": "插件提供自动加载 assts 资源能力", + "default": [], + "examples": [ + [ + [ + "build-plugin-load-assets", + { + "assets": ["https://unpkg.com/lodash@4.17.11/index.js", "https://url/global.css"] + } + ] + ] + ], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "插件提供自动加载 assts 资源能力", + "default": "", + "examples": ["plugin-load-assets"] + }, + { + "type": "object", + "description": "plugin-load-assets 设置.", + "default": {}, + "examples": [ + { + "assets": ["https://unpkg.com/lodash@4.17.11/index.js", "https://url/global.css"] + } + ], + "additionalProperties": true, + "properties": { + "assets": { + "type": "array", + "description": "Load assets from url", + "default": "", + "items": { + "type": "string" + } + } + } + } + ] + } + }, + { + "type": "string", + "description": "用于 debug 调试,常见于线上环境加载本地 assets 资源进行调试的场景:", + "default": "", + "examples": ["build-plugin-smart-debug"] + }, + { + "type": "string", + "description": "该插件支持了一种 JSX 扩展语法 JSX+,它能帮助业务开发者更爽更快地书写 JSX。JSX+ 不是一种新的概念,它是 JSX 基础上的扩展指令概念", + "default": "", + "examples": ["build-plugin-jsx-plus"] + }, + { + "type": "string", + "description": "该插件是对 react-refresh 的集成,一个类似 hot reloading(热更新)的功能,但提供了更为强大的快热更新机制,让你对 React 组件中的更改获得近乎即时(1 ~ 2 秒)的反馈,并且当修改 React 组件发生热更新时不会丢失原有的状态", + "default": "", + "examples": ["build-plugin-fast-refresh"] + }, + { + "type": "string", + "description": "用于 icejs 在使用 webpack5 能力上的兼容处理,目前 webpack 5 还未正式发布,请谨慎使用该插件", + "default": "", + "examples": ["build-plugin-webpack5"] + } + ] + } + } + } +} diff --git a/extensions/iceworks-config-helper/schemas/rax.app.en.json b/extensions/iceworks-config-helper/schemas/rax.app.en.json new file mode 100644 index 000000000..cb88495a8 --- /dev/null +++ b/extensions/iceworks-config-helper/schemas/rax.app.en.json @@ -0,0 +1,226 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "title": "Rax app.json", + "description": "Edit the app.json through this panel visually", + "default": {}, + "examples": [{}], + "required": [], + "properties": { + "routes": { + "type": "array", + "description": "Route Config", + "default": [], + "examples": [ + [ + { + "path": "/", + "source": "pages/Home/index" + } + ] + ], + "items": { + "anyOf": [ + { + "type": "object" + } + ] + } + }, + "hydrate": { + "type": "boolean", + "description": "Determines the original DOM structure is retained and event bindings are added when rendering content.(Only in web application). more: https://rax.js.org/docs/api/hydrate", + "default": false + }, + "shell": { + "type": "object", + "description": "Config PWA shell, recommend setting hydrate:true. (Only in web)", + "default": {}, + "examples": [{ "source": "shell/index" }] + }, + "window": { + "type": "object", + "description": "Set the application window", + "default": {}, + "properties": { + "titleBarColor": { + "type": "string", + "description": "title bar color, using decimal color value,range: (0-255) (Only for mini programs) ", + "examples": [255] + }, + "pullRefresh": { + "type": "boolean", + "description": "determines whether dropdown refresh is allowed. (Only for mini programs)", + "default": false + }, + "title": { + "type": "string", + "description": "title for web and mini programs", + "default": "", + "examples": ["title"] + }, + "defaultTitle": { + "type": "string", + "description": "default title for mini programs", + "default": "", + "examples": ["title"] + }, + "allowsBounceVertical": { + "type": "boolean", + "description": "determines whether pull down is allowed. ", + "default": false + }, + "transparentTitle": { + "type": "string", + "description": "determines whether The navigation bar is transparent. ", + "default": "none", + "enum": ["none", "always", "auto"] + }, + "titlePenetrate": { + "type": "string", + "description": "Allow navigation bar to click through ", + "default": "NO", + "enum": ["YES", "NO"] + }, + "showTitleLoading": { + "type": "string", + "description": "use the loading page before displays the navigation bar ", + "default": "NO", + "enum": ["YES", "NO"] + }, + "titleImage": { + "type": "string", + "description": "Navigation bar pic url ", + "default": "", + "examples": ["https://img.alicdn.com/tfs/TB1iCt_bVP7gK0jSZFjXXc5aXXa-82-30.png"] + }, + "backgroundColor": { + "type": "string", + "description": "the background color. ", + "default": "", + "examples": ["#FFFFFF"] + }, + "backgroundImageColor": { + "type": "string", + "description": "the background color when user pull down", + "default": "", + "examples": ["#FFFFFF"] + }, + "backgroundImageUrl": { + "type": "string", + "description": "the background image url when user pull down", + "default": "", + "examples": ["#FFFFFF"] + }, + "gestureBack": { + "type": "string", + "description": " Whether gesture returns are supported. (Only for IOS)", + "default": "NO", + "enum": ["YES", "NO"] + }, + "onReachBottomDistance": { + "type": "string", + "description": " Whether to display the webview scroll bar.. (Only for Andriod)", + "default": "NO", + "enum": ["YES", "NO"] + }, + "enableScrollBar": { + "type": "string", + "description": "The default is true. When set to false, the 2RPX will be identical to 1px and no longer adaptive to the width of the screen. Note that the 750rpx will no longer be equal to 100% width.", + "default": "", + "examples": ["50px"] + }, + "responsive": { + "type": "boolean", + "description": "The distance from the bottom of the page when the touchdown is triggered (px)", + "default": true, + "examples": ["50px"] + } + } + }, + "tarBar": { + "type": "object", + "description": "The tabBar configuration item specifies the appearance of the TAB bar and the corresponding page to be displayed when the TAB switches.", + "default": {}, + "properties": { + "textColor": { + "type": "string", + "description": "Text color (Hex Color) ", + "default": "", + "examples": ["#dddddd"] + }, + "selectedColor": { + "type": "string", + "description": "Selected text color (Hex Color) ", + "default": "", + "examples": ["#dddddd"] + }, + "backgroundColor": { + "type": "string", + "description": "Background color (Hex Color)", + "default": "", + "examples": ["#dddddd"] + }, + "items": { + "type": "array", + "description": "Settings for each tab ", + "examples": ["name"], + "default": [ + { + "pagePath": "", + "name": "" + } + ], + "items": { + "type": "object", + "properties": { + "pagePath": { + "type": "string", + "description": " tab path (shoud be the same in route) (Only for mini programs)", + "default": "", + "examples": ["pages/index/index"] + }, + "name": { + "type": "string", + "description": " tab name", + "default": "", + "examples": ["Home Page"] + }, + "icon": { + "type": "string", + "description": "icon (non-selected)", + "default": "", + "examples": ["logo.png"] + }, + "activeIcon": { + "type": "string", + "description": "icon (selected)", + "default": "", + "examples": ["activeLogo.png"] + } + } + } + } + }, + "examples": [ + { + "tabBar": { + "textColor": "#dddddd", + "selectedColor": "#49a9ee", + "backgroundColor": "#ffffff", + "items": [ + { + "pagePath": "pages/index/index", + "name": "Home Page" + }, + { + "pagePath": "pages/logs/logs", + "name": "Logs" + } + ] + } + } + ] + } + } +} diff --git a/extensions/iceworks-config-helper/schemas/rax.app.zh-cn.json b/extensions/iceworks-config-helper/schemas/rax.app.zh-cn.json new file mode 100644 index 000000000..d96890528 --- /dev/null +++ b/extensions/iceworks-config-helper/schemas/rax.app.zh-cn.json @@ -0,0 +1,226 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "title": "Rax app.json", + "description": "Application configuration for rax-app", + "default": {}, + "examples": [{}], + "required": [], + "properties": { + "routes": { + "type": "array", + "description": "路由配置", + "default": [], + "examples": [ + [ + { + "path": "/", + "source": "pages/Home/index" + } + ] + ], + "items": { + "anyOf": [ + { + "type": "object" + } + ] + } + }, + "hydrate": { + "type": "boolean", + "description": "渲染内容时是否保留原有 DOM 结构并补充事件绑定,详见 https://rax.js.org/docs/api/hydrate", + "default": false + }, + "shell": { + "type": "object", + "description": "设置 PWA AppShell,详见 https://rax.js.org/docs/guide/web-app-shell", + "default": {}, + "examples": [{ "source": "shell/index" }] + }, + "window": { + "type": "object", + "description": "设置应用窗口表现", + "default": {}, + "properties": { + "titleBarColor": { + "type": "string", + "description": "标题栏颜色,使用十进制颜色值,范围:(0-255) (只在小程序里使用) ", + "examples": [255] + }, + "pullRefresh": { + "type": "boolean", + "description": "是否允许下拉刷新,默认 true。(只在小程序里使用)", + "default": false + }, + "title": { + "type": "string", + "description": "标题 (可在小程序和网页里使用)", + "default": "", + "examples": ["title"] + }, + "defaultTitle": { + "type": "string", + "description": "页面默认标题。", + "default": "", + "examples": ["title"] + }, + "allowsBounceVertical": { + "type": "boolean", + "description": "是否允许向下拉拽。默认 YES, 支持 YES / NO ", + "default": false + }, + "transparentTitle": { + "type": "string", + "description": "导航栏透明设置。默认 none,支持 always 一直透明 / auto 滑动自适应 / none 不透明 ", + "default": "none", + "enum": ["none", "always", "auto"] + }, + "titlePenetrate": { + "type": "string", + "description": "是否允许导航栏点击穿透。默认 NO,支持 YES / NO。", + "default": "NO", + "enum": ["YES", "NO"] + }, + "showTitleLoading": { + "type": "string", + "description": "是否进入时显示导航栏的 loading。默认 NO,支持 YES / NO。", + "default": "NO", + "enum": ["YES", "NO"] + }, + "titleImage": { + "type": "string", + "description": "导航栏图片地址。", + "default": "", + "examples": ["https://img.alicdn.com/tfs/TB1iCt_bVP7gK0jSZFjXXc5aXXa-82-30.png"] + }, + "backgroundColor": { + "type": "string", + "description": "页面的背景色。例:白色 \"#FFFFFF\"。", + "default": "", + "examples": ["#FFFFFF"] + }, + "backgroundImageColor": { + "type": "string", + "description": "下拉露出显示背景图的底色。例:白色 \"#FFFFFF\"。", + "default": "", + "examples": ["#FFFFFF"] + }, + "backgroundImageUrl": { + "type": "string", + "description": "下拉露出显示背景图的链接。", + "default": "", + "examples": ["#FFFFFF"] + }, + "gestureBack": { + "type": "string", + "description": "仅支持 iOS,是否支持手势返回。默认 YES,支持 YES / NO。", + "default": "NO", + "enum": ["YES", "NO"] + }, + "onReachBottomDistance": { + "type": "string", + "description": "页面上拉触底时触发时距离页面底部的距离,单位为px。相关文档页面事件处理函数。", + "default": "NO", + "enum": ["YES", "NO"] + }, + "enableScrollBar": { + "type": "string", + "description": " 仅支持 Android,是否显示 WebView 滚动条。默认 YES,支持 YES / NO。", + "default": "", + "examples": ["50px"] + }, + "responsive": { + "type": "boolean", + "description": " rpx 单位是否宽度自适应 ,默认true,当设置为 false 时,2rpx 将恒等于 1px,不再根据屏幕宽度进行自适应,注意,此时 750rpx 将不再等于100% 宽度。", + "default": true, + "examples": ["50px"] + } + } + }, + "tarBar": { + "type": "object", + "description": "设置应用底部 TabBar 的表现。", + "default": {}, + "properties": { + "textColor": { + "type": "string", + "description": "文字颜色", + "default": "", + "examples": ["#dddddd"] + }, + "selectedColor": { + "type": "string", + "description": "选中文字颜色", + "default": "", + "examples": ["#dddddd"] + }, + "backgroundColor": { + "type": "string", + "description": "背景色", + "default": "", + "examples": ["#dddddd"] + }, + "items": { + "type": "array", + "description": "每个 tab 的设置", + "": ["name"], + "default": [ + { + "pagePath": "", + "name": "" + } + ], + "items": { + "type": "object", + "properties": { + "pagePath": { + "type": "string", + "description": " 设置页面路径", + "default": "", + "examples": ["pages/index/index"] + }, + "name": { + "type": "string", + "description": " 名称", + "default": "", + "examples": ["首页"] + }, + "icon": { + "type": "string", + "description": "平常图标路径(非选中状态)", + "default": "", + "examples": ["logo.png"] + }, + "activeIcon": { + "type": "string", + "description": "高亮图标路径(选中状态)", + "default": "", + "examples": ["activeLogo.png"] + } + } + } + } + }, + "examples": [ + { + "tabBar": { + "textColor": "#dddddd", + "selectedColor": "#49a9ee", + "backgroundColor": "#ffffff", + "items": [ + { + "pagePath": "pages/index/index", + "name": "首页" + }, + { + "pagePath": "pages/logs/logs", + "name": "日志" + } + ] + } + } + ] + } + } +} diff --git a/extensions/iceworks-config-helper/schemas/rax.build.json b/extensions/iceworks-config-helper/schemas/rax.build.en.json similarity index 74% rename from extensions/iceworks-config-helper/schemas/rax.build.json rename to extensions/iceworks-config-helper/schemas/rax.build.en.json index 85ad139f3..e2134f36d 100644 --- a/extensions/iceworks-config-helper/schemas/rax.build.json +++ b/extensions/iceworks-config-helper/schemas/rax.build.en.json @@ -2,7 +2,7 @@ "definitions": {}, "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", - "title": "The Rax APP build.json Schema", + "title": "rax-app build.json", "required": ["plugins"], "properties": { "publicPath": { @@ -39,6 +39,7 @@ "devServer": { "type": "object", "description": "This set of options is picked up by webpack-dev-server and can be used to change its behavior in various ways.", + "default": {}, "properties": { "compress": { "type": "boolean", @@ -82,6 +83,7 @@ "type": "array", "description": "Compiler plugins", "additionalItems": true, + "default": [], "items": { "anyOf": [ { @@ -113,23 +115,37 @@ "items": [ { "type": "string", - "description": "Rax PHA Plugin", - "enum": ["build-plugin-rax-pha"] + "description": "Rax PWA plugin", + "enum": ["build-plugin-rax-pwa"] }, { "type": "object", "properties": { + "manifest": { + "type": "object", + "description": "Browsers can save PWA-enabled websites to the desktop for a smaller size and more powerful functionality than native apps. It can have a splash screen like a native APP, or a full screen display like a native APP.", + "default": {}, + "additionalProperties": true + }, "snapshot": { "type": "boolean", - "description": "Render element snapshot, see https://rax.js.org/docs/en-guide/pwa-snapshot", + "description": "The snapshot can fill the HTML container with the content corresponding to the last time the user visited it. It makes displaying page content in a high speed, and improves the loading speed of the first screen.", "default": false + }, + "serviceWorker": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "Use the Service Worker to control the cache for faster loading times." } } } ], "examples": [ [ - "build-plugin-rax-pha", + "build-plugin-rax-pwa", { "snapshot": true } @@ -141,7 +157,12 @@ }, { "type": "string", - "examples": ["build-plugin-rax-compat-react", "build-plugin-rax-ssr", "build-plugin-rax-multi-pages"] + "examples": [ + "build-plugin-rax-compat-react", + "build-plugin-rax-ssr", + "build-plugin-rax-multi-pages", + "build-plugin-rax-component" + ] } ] } diff --git a/extensions/iceworks-config-helper/schemas/rax.build.zh-cn.json b/extensions/iceworks-config-helper/schemas/rax.build.zh-cn.json new file mode 100644 index 000000000..c6a9f3d08 --- /dev/null +++ b/extensions/iceworks-config-helper/schemas/rax.build.zh-cn.json @@ -0,0 +1,171 @@ +{ + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "rax-app 的工程配置", + "required": ["plugins"], + "properties": { + "publicPath": { + "type": "string", + "description": "配置 webpack 的 output.publicPath 属性。 仅在运行 build 命令时生效。", + "default": "/" + }, + "devPublicPath": { + "type": "string", + "description": "配置 webpack 的 output.publicPath 属性。 仅在运行 start 命令时生效。", + "default": "/" + }, + "outputDir": { + "type": "string", + "description": "修改构建后的文件目录", + "default": "", + "examples": ["build"] + }, + "hash": { + "type": "boolean", + "description": "如果希望构建后的资源带 hash 版本,可以将 hash 设置为 true", + "default": false + }, + "alias": { + "type": "object", + "description": "使用别名简化引入语句", + "default": {}, + "examples": [ + { + "@components": "src/components/" + } + ] + }, + "devServer": { + "type": "object", + "description": " webpack-dev-server 将采用这个选项下的内容,你可以配置多项属性来进行定制", + "default": {}, + "properties": { + "compress": { + "type": "boolean", + "description": "为所有服务启用 gzip 压缩", + "default": false + }, + "disableHostCheck": { + "type": "boolean", + "description": "当设置为真时,此选项绕过主机检查。我们不建议这样做,因为不检查主机的应用程序容易受到DNS重新绑定攻击。", + "default": false + }, + "clientLogLevel": { + "type": "string", + "description": "DevTools中的控制台将显示不同级别的消息。", + "default": "error", + "pattern": "^(info|silent|trace|debug|info|warn|error|none|warning)$" + }, + "hot": { + "type": "boolean", + "description": "启用 webpack 的热模块替换功能", + "default": false + }, + "quiet": { + "type": "boolean", + "description": "是否设置静音。启用静音后,除了初始启动信息外,不会向控制台写入任何内容。", + "default": false + }, + "overlay": { + "type": "boolean", + "description": "当出现编译器错误或警告时,在浏览器中显示全屏覆盖。这将只显示编译器错误。", + "default": false + }, + "port": { + "type": "integer", + "description": "监听的端口号", + "default": 9999 + } + } + }, + "plugins": { + "type": "array", + "description": "编译插件", + "additionalItems": true, + "default": [], + "items": { + "anyOf": [ + { + "type": "array", + "items": [ + { + "type": "string", + "description": "编译 Rax App", + "enum": ["build-plugin-rax-app"] + }, + { + "type": "object", + "properties": { + "targets": { + "type": "array", + "description": "项目运行的平台", + "items": { + "type": "string", + "enum": ["web", "weex", "miniapp", "wechat-miniprogram", "kraken"] + } + } + }, + "required": ["targets"] + } + ] + }, + { + "type": "array", + "items": [ + { + "type": "string", + "description": "Rax PWA 插件", + "enum": ["build-plugin-rax-pwa"] + }, + { + "type": "object", + "properties": { + "manifest": { + "type": "object", + "description": "浏览器可以将支持 PWA 的网站保存至桌面,获得比原生应用更轻巧的体积和比 Web 应用更强大的功能。它能拥有原生 APP 应用一般的启动闪屏,也能像原生 APP 应用一样全屏展示。", + "default": {}, + "additionalProperties": true + }, + "snapshot": { + "type": "boolean", + "description": "节点快照可将 HTML 容器填充为用户上一次访问时对应的内容,极速展示页面内容,提升首屏加载速度。", + "default": false + }, + "serviceWorker": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "使用 Service Worker 控制缓存,以获得更快的加载速度。" + } + } + } + ], + "examples": [ + [ + "build-plugin-rax-pwa", + { + "snapshot": true + } + ] + ] + }, + { + "type": "array" + }, + { + "type": "string", + "examples": [ + "build-plugin-rax-compat-react", + "build-plugin-rax-ssr", + "build-plugin-rax-multi-pages", + "build-plugin-rax-component" + ] + } + ] + } + } + } +} diff --git a/extensions/iceworks-config-helper/src/extension.ts b/extensions/iceworks-config-helper/src/extension.ts new file mode 100644 index 000000000..74b4bf7f3 --- /dev/null +++ b/extensions/iceworks-config-helper/src/extension.ts @@ -0,0 +1,136 @@ +import * as vscode from 'vscode'; +import { connectService, getHtmlForWebview } from '@iceworks/vscode-webview/lib/vscode'; +import { initExtension } from '@iceworks/common-service'; +import { recordDAU, Recorder } from '@iceworks/recorder'; +import { getProjectFramework } from '@iceworks/project-service'; +import { setEditingJsonFile, services, getIsUpdatingJsonFile } from './services'; +import i18n from './i18n'; + +// eslint-disable-next-line +const { name, version } = require('../package.json'); +const recorder = new Recorder(name, version); + +export async function activate(context: vscode.ExtensionContext) { + await setJsonValidationUrl(); + recorder.recordActivate(); + + const { extensionPath, subscriptions } = context; + + initExtension(context); + + let configWebviewPanel: vscode.WebviewPanel | undefined; + + function activeConfigWebview(JsonFileName: string) { + recordDAU(); + recorder.record({ + module: 'main', + action: 'activeConfigWebview', + }); + + setEditingJsonFile(JsonFileName); + + if (configWebviewPanel) { + configWebviewPanel.dispose(); + } + configWebviewPanel = vscode.window.createWebviewPanel( + 'iceworks', + i18n.format('extension.iceworksConfigHelper.index.webviewTitle'), + vscode.ViewColumn.Two, + { + enableScripts: true, + retainContextWhenHidden: true, + } + ); + configWebviewPanel.webview.html = getHtmlForWebview(extensionPath); + configWebviewPanel.onDidDispose( + () => { + configWebviewPanel = undefined; + }, + null, + context.subscriptions + ); + connectService(configWebviewPanel, context, { services, recorder }); + } + + activePanelEntry(); + vscode.window.onDidChangeActiveTextEditor(() => { + activePanelEntry(); + }); + + subscriptions.push( + vscode.commands.registerCommand('iceworks-config-helper.buildJson.start', () => { + activeConfigWebview('build'); + }), + vscode.commands.registerCommand('iceworks-config-helper.appJson.start', () => { + activeConfigWebview('app'); + }) + ); + + subscriptions.push( + vscode.workspace.onDidChangeTextDocument((event) => { + if ( + configWebviewPanel && + isConfigJson(event.document, ['build.json', 'app.json']) && + event.contentChanges.length > 0 && + !getIsUpdatingJsonFile() + ) { + console.log('do update webview Json', event.contentChanges); + try { + const jsonContent = JSON.parse(event.document.getText()); + configWebviewPanel.webview.postMessage({ + command: 'iceworks-config-helper:updateJson', + jsonContent, + }); + } catch (error) { + // ignore + } + } + }) + ); +} + +function activePanelEntry() { + const currentActiveEditor = vscode.window.activeTextEditor; + if (!currentActiveEditor) return; + if (currentActiveEditor?.document.uri.fsPath.endsWith('build.json')) { + vscode.commands.executeCommand('setContext', 'iceworks:showWebViewPanelForBuildJson', true); + vscode.commands.executeCommand('setContext', 'iceworks:showWebViewPanelForAppJson', false); + } else if (currentActiveEditor?.document.uri.fsPath.endsWith('app.json')) { + vscode.commands.executeCommand('setContext', 'iceworks:showWebViewPanelForAppJson', true); + vscode.commands.executeCommand('setContext', 'iceworks:showWebViewPanelForBuildJson', false); + } else { + vscode.commands.executeCommand('setContext', 'iceworks:showWebViewPanelForBuildJson', false); + vscode.commands.executeCommand('setContext', 'iceworks:showWebViewPanelForAppJson', false); + } +} + +async function setJsonValidationUrl() { + try { + const projectFramework = await getProjectFramework(); + + vscode.extensions.all.forEach((extension) => { + if (extension.id !== 'iceworks-team.iceworks-config-helper') { + return; + } + + const packageJson = extension.packageJson; + if (packageJson && packageJson.contributes && (projectFramework === 'rax-app' || projectFramework === 'icejs')) { + const jsonValidation = packageJson.contributes.jsonValidation; + jsonValidation[0].url = `./schemas/${projectFramework === 'icejs' ? 'ice' : 'rax'}.build.${ + vscode.env.language + }.json`; + if (projectFramework === 'rax-app') { + jsonValidation[1].url = `./schemas/rax.app.${vscode.env.language}.json`; + } + } + }); + } catch (e) { + // ignore + } +} + +function isConfigJson(document: vscode.TextDocument, filenames: string[]) { + return filenames.find((e) => { + return document.uri.fsPath.endsWith(e); + }); +} diff --git a/extensions/iceworks-config-helper/src/i18n.ts b/extensions/iceworks-config-helper/src/i18n.ts new file mode 100644 index 000000000..ee3f430a3 --- /dev/null +++ b/extensions/iceworks-config-helper/src/i18n.ts @@ -0,0 +1,12 @@ +import * as vscode from 'vscode'; +import I18nService from '@iceworks/i18n'; +import * as zhCNTextMap from './locales/zh-CN.json'; +import * as enUSTextMap from './locales/en-US.json'; + +const i18n = new I18nService(); + +i18n.registry('zh-cn', zhCNTextMap); +i18n.registry('en', enUSTextMap); +i18n.setLocal(vscode.env.language); + +export default i18n; diff --git a/extensions/iceworks-config-helper/src/index.ts b/extensions/iceworks-config-helper/src/index.ts deleted file mode 100644 index a88bfb544..000000000 --- a/extensions/iceworks-config-helper/src/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as vscode from 'vscode'; -import { getProjectFramework } from '@iceworks/project-service'; - -async function activate() { - try { - const projectFramework = await getProjectFramework(); - - vscode.extensions.all.forEach((extension) => { - if (extension.id !== 'iceworks-team.iceworks-config-helper') { - return; - } - - const packageJSON = extension.packageJSON; - if (packageJSON && packageJSON.contributes && (projectFramework === 'rax-app' || projectFramework === 'icejs')) { - const jsonValidation = packageJSON.contributes.jsonValidation; - jsonValidation[0].url = `./schemas/${projectFramework === 'icejs' ? 'ice' : 'rax'}.build.json`; - } - }); - } catch (e) { - // ignore - } -} - -exports.activate = activate; diff --git a/extensions/iceworks-config-helper/src/locales/en-US.json b/extensions/iceworks-config-helper/src/locales/en-US.json new file mode 100644 index 000000000..5e6b671ea --- /dev/null +++ b/extensions/iceworks-config-helper/src/locales/en-US.json @@ -0,0 +1,4 @@ +{ + "extension.iceworksConfigHelper.index.webviewTitle": "Config Helper - Iceworks", + "extension.iceworksConfigHelper.loadJson.JsonErr": " <%= editingJsonFile %>.json 文件不能解析,请改正后再试" +} diff --git a/extensions/iceworks-config-helper/src/locales/zh-CN.json b/extensions/iceworks-config-helper/src/locales/zh-CN.json new file mode 100644 index 000000000..db278fac5 --- /dev/null +++ b/extensions/iceworks-config-helper/src/locales/zh-CN.json @@ -0,0 +1,4 @@ +{ + "extension.iceworksConfigHelper.index.webviewTitle": "可视化配置 - Iceworks", + "extension.iceworksConfigHelper.loadJson.JsonErr": "Cannot parse <%= editingJsonFile %>.json, please fix it" +} diff --git a/extensions/iceworks-config-helper/src/services.ts b/extensions/iceworks-config-helper/src/services.ts new file mode 100644 index 000000000..a456f32aa --- /dev/null +++ b/extensions/iceworks-config-helper/src/services.ts @@ -0,0 +1,136 @@ +import * as vscode from 'vscode'; +import * as fse from 'fs-extra'; +import forIn from 'lodash.forin'; +import { getProjectFramework } from '@iceworks/project-service'; +import * as common from '@iceworks/common-service'; +import i18n from './i18n'; + +const buildJsonPath = `${vscode.workspace.rootPath}/build.json`; +const appJsonPath = `${vscode.workspace.rootPath}/src/app.json`; +const buildJsonUri = vscode.Uri.file(buildJsonPath); +const appJsonUri = vscode.Uri.file(appJsonPath); + +// witch Json file is editing +// now just support build.json & app.json, so just using the file name +let editingJsonFile; + +const getInitData = async () => { + // eslint-disable-next-line + const schema = require(`../schemas/${(await getProjectFramework()) === 'icejs' ? 'ice' : 'rax'}.${editingJsonFile}.${ + vscode.env.language + }.json`); + + const initmessage = { + jsonContent: getEditingJsonFileValue(), + schema, + formCannotEditProps: getFormCannotEditProps(schema), + editingJsonFile: `${editingJsonFile}.json`, + }; + + return initmessage; +}; + +// TODO 程序锁,标识是否正在更新 Json 文件 +// 当 webview 通知更新 Json 文件时,则在编辑器文本更新事件中不发送更新 webview Json 的指令,避免死循环 +let isUpdatingJsonFile = false; +export function getIsUpdatingJsonFile() { + return isUpdatingJsonFile; +} + +const updateJsonFile = async (incrementalChange) => { + const currentJsonTextEditor = findVisibleTextEditor(`${editingJsonFile}.json`); + const json = getEditingJsonByIncrementalChange(incrementalChange, false); + + if (currentJsonTextEditor) { + const end = new vscode.Position(currentJsonTextEditor.document.lineCount + 1, 0); + isUpdatingJsonFile = true; + await currentJsonTextEditor.edit((editor) => { + editor.replace(new vscode.Range(new vscode.Position(0, 0), end), JSON.stringify(json, null, '\t')); + }); + isUpdatingJsonFile = false; + } else { + fse.writeFile( + editingJsonFile === 'build' ? buildJsonPath : appJsonPath, + JSON.stringify(json, null, '\t'), + (err) => { + console.log(err); + } + ); + } +}; + +const editInJsonFile = (incrementalChange) => { + let currentJsonEditer = findVisibleTextEditor(`${editingJsonFile}.json`); + const json = getEditingJsonByIncrementalChange(incrementalChange, true); + + const currentKey = Object.keys(incrementalChange)[0]; + if (!currentJsonEditer) { + vscode.window.showTextDocument(editingJsonFile === 'build' ? buildJsonUri : appJsonUri, { + viewColumn: vscode.window.activeTextEditor?.viewColumn === 1 ? 2 : 1, + }); + currentJsonEditer = findVisibleTextEditor(`${editingJsonFile}.json `); + } + + // 使用 snippet 移动光标;具体的原理是更新整个 json 文件,并且插入光标占位符 + currentJsonEditer!.insertSnippet( + new vscode.SnippetString(JSON.stringify(json, undefined, '\t').replace(`"${currentKey}": `, `"${currentKey}": $1`)), + new vscode.Range(new vscode.Position(0, 0), new vscode.Position(currentJsonEditer!.document.lineCount + 1, 0)) + ); +}; + +export const services = { + config: { + getInitData, + updateJsonFile, + editInJsonFile, + }, + common, +}; + +export function setEditingJsonFile(name: string) { + editingJsonFile = name; +} + +function getEditingJsonFileValue() { + const currentJsonTextEditor = findVisibleTextEditor(`${editingJsonFile}.json`); + try { + if (currentJsonTextEditor) { + return JSON.parse(currentJsonTextEditor.document.getText()); + } else { + return fse.readJsonSync(editingJsonFile === 'build' ? buildJsonPath : appJsonPath); + } + } catch (e) { + console.error(e); + vscode.window.showWarningMessage( + i18n.format('extension.iceworksConfigHelper.loadJson.JsonErr', { JsonFileName: editingJsonFile }) + ); + } +} + +function findVisibleTextEditor(fileName: string) { + return vscode.window.visibleTextEditors.find((editor) => { + return editor.document.uri.fsPath.endsWith(fileName); + }); +} + +function getEditingJsonByIncrementalChange(incrementalChange, useSnippet: boolean) { + const json = getEditingJsonFileValue(); + forIn(incrementalChange, (value, key) => { + if (value === null) { + delete json[key]; + } else if (!useSnippet || (useSnippet && json[key] === undefined)) { + json[key] = value; + } + }); + return json; +} + +function getFormCannotEditProps(schema) { + const webViewCannotEditProps: string[] = []; + forIn(schema.properties, (value, key) => { + if (value.type === 'object' || value.type === 'array' || value.oneOf || value.anyOf || value.allOf) { + webViewCannotEditProps.push(key); + } + }); + return webViewCannotEditProps; +} diff --git a/extensions/iceworks-config-helper/tsconfig.json b/extensions/iceworks-config-helper/tsconfig.json index d2e97137b..1d45382a0 100644 --- a/extensions/iceworks-config-helper/tsconfig.json +++ b/extensions/iceworks-config-helper/tsconfig.json @@ -8,5 +8,6 @@ "resolveJsonModule": true, "esModuleInterop": true }, - "exclude": ["node_modules"] + "include": ["src", "schema"], + "exclude": ["node_modules", "web"] } diff --git a/extensions/iceworks-config-helper/web/README.md b/extensions/iceworks-config-helper/web/README.md new file mode 100644 index 000000000..894155264 --- /dev/null +++ b/extensions/iceworks-config-helper/web/README.md @@ -0,0 +1,17 @@ + +## Simple + +> A TypeScript simple template + +## 使用 + +```bash +# 安装依赖 +$ npm install + +# 启动服务 +$ npm start # visit http://localhost:3333 +``` + +[More docs](https://ice.work/docs/guide/about). + diff --git a/extensions/iceworks-config-helper/web/build.json b/extensions/iceworks-config-helper/web/build.json new file mode 100644 index 000000000..81682baa5 --- /dev/null +++ b/extensions/iceworks-config-helper/web/build.json @@ -0,0 +1,31 @@ +{ + "devServer": { + "writeToDisk": true + }, + "publicPath": "./", + "outputDir": "../build", + "plugins": [ + [ + "build-plugin-fusion", + { + "themePackage": [ + { + "name": "@alifd/theme-iceworks-dark" + } + ] + } + ], + [ + "build-plugin-moment-locales", + { + "locales": ["zh-cn"] + } + ], + [ + "build-plugin-css-assets-local", + { + "activeInDev": true + } + ] + ] +} diff --git a/extensions/iceworks-config-helper/web/mocks/common/getLanguage.ts b/extensions/iceworks-config-helper/web/mocks/common/getLanguage.ts new file mode 100644 index 000000000..4bb9a7424 --- /dev/null +++ b/extensions/iceworks-config-helper/web/mocks/common/getLanguage.ts @@ -0,0 +1 @@ +export default 'zh-cn'; diff --git a/extensions/iceworks-config-helper/web/mocks/config/editInJson.ts b/extensions/iceworks-config-helper/web/mocks/config/editInJson.ts new file mode 100644 index 000000000..1dcea4aed --- /dev/null +++ b/extensions/iceworks-config-helper/web/mocks/config/editInJson.ts @@ -0,0 +1,3 @@ +export default (args) => { + console.log('receive from webview', args); +}; diff --git a/extensions/iceworks-config-helper/web/mocks/config/getInitData.ts b/extensions/iceworks-config-helper/web/mocks/config/getInitData.ts new file mode 100644 index 000000000..1dffde4b3 --- /dev/null +++ b/extensions/iceworks-config-helper/web/mocks/config/getInitData.ts @@ -0,0 +1,11 @@ +import schema from './ice.build.json'; +import jsonContent from '../mockBuild.json'; + +const mockInitData = { + schema, + formCannotEditProps: ['alias', 'hash', 'compileDependencies'], + jsonContent, + editingJsonFile: 'build.json', +}; + +export default mockInitData; diff --git a/extensions/iceworks-config-helper/web/mocks/config/ice.build.json b/extensions/iceworks-config-helper/web/mocks/config/ice.build.json new file mode 100644 index 000000000..aeb19f43e --- /dev/null +++ b/extensions/iceworks-config-helper/web/mocks/config/ice.build.json @@ -0,0 +1,83 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "title": "icejs build.json", + "description": "icejs 的工程配置", + "default": {}, + "examples": [{}], + "required": [], + "properties": { + "alias": { + "type": "object", + "description": "这是一个对象。", + "default": {}, + "examples": [ + { + "@components": "src/components/" + } + ] + }, + "publicPath": { + "type": "string", + "description": "这是一个字符串。", + "default": "", + "examples": ["/"] + }, + "sourceMap": { + "type": "boolean", + "description": "这是一个布尔值。", + "examples": [false] + }, + "hash": { + "oneOf": [ + { + "type": "string", + "examples": ["contenthash"] + }, + { + "type": "boolean", + "default": false, + "examples": [false] + } + ], + "description": "这是一个 oneOf。", + "default": false, + "properties": {} + }, + "outputDir": { + "type": "string", + "description": "这是一个字符串。", + "default": "", + "examples": ["build"] + }, + "vendor": { + "type": "boolean", + "description": "这是一个布尔值。", + "default": true, + "examples": [true] + }, + "libraryTarget": { + "type": "string", + "description": "这是一个字符串。", + "default": "", + "examples": [""] + }, + "compileDependencies": { + "type": "array", + "description": "这是一个数组。", + "default": [], + "examples": [["@alifd/next"]], + "additionalItems": true, + "items": { + "anyOf": [ + { + "type": "string", + "description": "Compile module dependencies under node_modules.", + "default": "", + "examples": ["@alifd/next"] + } + ] + } + } + } +} diff --git a/extensions/iceworks-config-helper/web/mocks/config/updateJsonFile.ts b/extensions/iceworks-config-helper/web/mocks/config/updateJsonFile.ts new file mode 100644 index 000000000..1dcea4aed --- /dev/null +++ b/extensions/iceworks-config-helper/web/mocks/config/updateJsonFile.ts @@ -0,0 +1,3 @@ +export default (args) => { + console.log('receive from webview', args); +}; diff --git a/extensions/iceworks-config-helper/web/mocks/mockBuild.json b/extensions/iceworks-config-helper/web/mocks/mockBuild.json new file mode 100644 index 000000000..ebf797510 --- /dev/null +++ b/extensions/iceworks-config-helper/web/mocks/mockBuild.json @@ -0,0 +1,3 @@ +{ + "vendor": true +} diff --git a/extensions/iceworks-config-helper/web/package.json b/extensions/iceworks-config-helper/web/package.json new file mode 100644 index 000000000..8fba3715e --- /dev/null +++ b/extensions/iceworks-config-helper/web/package.json @@ -0,0 +1,38 @@ +{ + "name": "web", + "version": "0.1.0", + "description": "web for iceworks/config-helper。", + "private": true, + "devDependencies": { + "@types/lodash": "^4.14.158", + "@types/react": "^16.9.20", + "@types/react-dom": "^16.9.5", + "build-plugin-css-assets-local": "^0.1.0", + "build-plugin-fusion": "^0.1.3", + "build-plugin-moment-locales": "^0.1.0", + "@alifd/theme-iceworks-dark": "^1.3.0", + "ice.js": "^1.0.0", + "typescript": "^3.8.3", + "node-sass": "4.14.1" + }, + "dependencies": { + "@alifd/next": "~1.19.2", + "@iceworks/vscode-webview": "^0.1.5", + "@rjsf/core": "^2.2.2", + "lodash": "^4.17.19", + "react": "^16.4.1", + "react-dom": "^16.4.1", + "react-intl": "^5.4.5" + }, + "scripts": { + "start": "icejs start", + "build": "icejs build" + }, + "repository": { + "type": "git", + "url": "https://github.com/ice-lab/iceworks.git" + }, + "engines": { + "node": ">=8.0.0" + } +} diff --git a/extensions/iceworks-config-helper/web/public/favicon.png b/extensions/iceworks-config-helper/web/public/favicon.png new file mode 100644 index 000000000..cff1ee57c Binary files /dev/null and b/extensions/iceworks-config-helper/web/public/favicon.png differ diff --git a/extensions/iceworks-config-helper/web/public/index.html b/extensions/iceworks-config-helper/web/public/index.html new file mode 100644 index 000000000..990d51e26 --- /dev/null +++ b/extensions/iceworks-config-helper/web/public/index.html @@ -0,0 +1,13 @@ + + + + + + + icejs simple app + + + +
+ + diff --git a/extensions/iceworks-config-helper/web/screenshot.png b/extensions/iceworks-config-helper/web/screenshot.png new file mode 100644 index 000000000..80dd6d97c Binary files /dev/null and b/extensions/iceworks-config-helper/web/screenshot.png differ diff --git a/extensions/iceworks-config-helper/web/src/app.ts b/extensions/iceworks-config-helper/web/src/app.ts new file mode 100644 index 000000000..d36476161 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/app.ts @@ -0,0 +1,9 @@ +import { createApp } from 'ice'; + +const appConfig = { + app: { + rootId: 'ice-container', + }, +}; + +createApp(appConfig); diff --git a/extensions/iceworks-config-helper/web/src/callService.ts b/extensions/iceworks-config-helper/web/src/callService.ts new file mode 100644 index 000000000..425243a9c --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/callService.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +import { callService as originCallService } from '@iceworks/vscode-webview/lib/webview'; + +export default async function callService(service: string, method: string, ...args) { + // @ts-ignore + if (typeof acquireVsCodeApi === 'function') { + return await originCallService.apply(null, arguments); + } else { + return require(`../mocks/${service}/${method}`).default; + } +} diff --git a/extensions/iceworks-config-helper/web/src/global.scss b/extensions/iceworks-config-helper/web/src/global.scss new file mode 100644 index 000000000..eda947054 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/global.scss @@ -0,0 +1,27 @@ +body { + -webkit-font-smoothing: antialiased; + margin: 0; + background: #1e1e1e; +} +.fddescription { + font-size: 12px; + color: rgba(204, 204, 204, 0.9); +} +h3 { + margin-top: 10px; + margin-bottom: 1px; + font-size: 16px; + color: #e7e7e7; +} +a { + font-size: 10px; + text-decoration: underline; +} +a:hover { + color: white; + text-decoration: none; + cursor: pointer; +} +.panel-body { + display: none; +} diff --git a/extensions/iceworks-config-helper/web/src/i18n.tsx b/extensions/iceworks-config-helper/web/src/i18n.tsx new file mode 100644 index 000000000..a82e9b0f7 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/i18n.tsx @@ -0,0 +1,60 @@ +import { createIntl, RawIntlProvider } from 'react-intl'; +import React, { useEffect, useState } from 'react'; +import { ConfigProvider, Loading } from '@alifd/next'; +import enUSNextMessages from '@alifd/next/lib/locale/en-us'; +import zhCNNextMessages from '@alifd/next/lib/locale/zh-cn'; + +import callService from './callService'; +import enUSMessages from './locales/en-US.json'; +import zhCNMessages from './locales/zh-CN.json'; + +const DEFAULT_LOCALE = 'zh-cn'; + +export const localeMessages = { + en: { + messages: enUSMessages, + nextMessages: enUSNextMessages, + }, + 'zh-cn': { + messages: zhCNMessages, + nextMessages: zhCNNextMessages, + }, +}; + +const getIntl = (locale: string) => { + let localeMessage = localeMessages[locale]; + if (!localeMessage) { + locale = DEFAULT_LOCALE; + localeMessage = localeMessages[locale]; + } + return createIntl({ locale, messages: localeMessage.messages }); +}; +export const LocaleProvider = (props) => { + const [i18n, setI18n] = useState(() => getIntl(DEFAULT_LOCALE)); + const [loading, setLoading] = useState(true); + useEffect(() => { + async function initI18n() { + try { + const lang = await callService('common', 'getLanguage'); + setI18n(getIntl(lang)); + } catch (e) { + // ignore i18n error, just using default language + } finally { + setLoading(false); + } + } + initI18n(); + }, []); + + return ( + + + {loading ? ( + + ) : ( + React.Children.only(props.children) + )} + + + ); +}; diff --git a/extensions/iceworks-config-helper/web/src/locales/en-US.json b/extensions/iceworks-config-helper/web/src/locales/en-US.json new file mode 100644 index 000000000..1eb6e28eb --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/locales/en-US.json @@ -0,0 +1,4 @@ +{ + "web.iceworksConfigHelper.index.settingFileNotReady": "Setting file is not valiable now... Please fix it and retry.", + "web.iceworksConfigHelper.editInJsonFile.editInJsonLink": "Edit in {editingJsonFile}." +} diff --git a/extensions/iceworks-config-helper/web/src/locales/zh-CN.json b/extensions/iceworks-config-helper/web/src/locales/zh-CN.json new file mode 100644 index 000000000..730ac3bdb --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/locales/zh-CN.json @@ -0,0 +1,4 @@ +{ + "web.iceworksConfigHelper.index.settingFileNotReady": "不能加载 Json 配置文件... 请修复后重试.", + "web.iceworksConfigHelper.editInJsonFile.editInJsonLink": "在 {editingJsonFile} 中编辑" +} diff --git a/extensions/iceworks-config-helper/web/src/pages/JsonForm/index.tsx b/extensions/iceworks-config-helper/web/src/pages/JsonForm/index.tsx new file mode 100644 index 000000000..efeed9dab --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/pages/JsonForm/index.tsx @@ -0,0 +1,158 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useState, useEffect, memo, useRef } from 'react'; +import Form from '@rjsf/core'; +import { Card, Loading } from '@alifd/next'; +import * as _ from 'lodash'; +import { useIntl } from 'react-intl'; +import { fields, widgets, templates } from '@/theme/theme'; +import { LocaleProvider } from '@/i18n'; +import callService from '../../callService'; +import { createIncremetalUpdate, getSchemaDefaultValue, getUISchema } from '../../utils'; + +const CARD_STYLE = { background: '#1e1e1e' }; +const LOADING_STYLE = { width: '100%', height: '80vh' }; + +// TODO 程序锁,标识是是否正在更新 Json +// 当编辑器发生 Json 变更时,触发 Form 的 rerender,但不触发表单的 onChange,避免死循环 +let isUpdatingJson = false; + +const JsonSchemaForm = ({ jsonContent, schema, uiSchema, setData }) => { + console.log('render JsonSchemaForm', jsonContent); + const setJson = async (e) => { + console.log('onChange...'); + if (!isUpdatingJson) { + console.log('do setData...'); + setData(e); + } + }; + + return ( +
setJson(e.formData)} + > + <> +
+ ); +}; + +const MemoJsonSchemaForm = memo(JsonSchemaForm, _.isEqual); +export const configHelperProvider = React.createContext({ + defaultSchema: {}, + jsonContent: {}, + editingJsonFile: '', +}); + +const JsonForm = () => { + const intl = useIntl(); + const [loading, setLoading] = useState(true); + const [currentSchema, setCurrentSchema] = useState({}); + const [jsonContent, orginSetJsonContent] = useState({}); + const editingJsonFile = useRef('build.json'); + const uischema = useRef({}); + const formCannotEditProps = useRef([]); + const schemaDefaultValue = useRef({}); + + function setJsonContent(value) { + isUpdatingJson = true; + orginSetJsonContent(value); + } + + useEffect(() => { + isUpdatingJson = false; + }, [jsonContent]); + + useEffect(() => { + window.addEventListener( + 'message', + (e) => { + const { command, jsonContent } = e.data; + if (command === 'iceworks-config-helper:updateJson') { + console.log('got updateJson message, jsonContent:', jsonContent); + setJsonContent(jsonContent); + } + }, + false + ); + }, []); + + async function setSyncAndUpdateJsonFile(newData) { + const { incrementalChange, newSyncJson } = createIncremetalUpdate( + newData, + jsonContent, + schemaDefaultValue.current, + formCannotEditProps.current + ); + + console.log('newSyncJson', newSyncJson); + console.log('incrementalChange', incrementalChange); + + if (Object.keys(incrementalChange).length !== 0) { + setJsonContent(newSyncJson); + await callService('config', 'updateJsonFile', incrementalChange); + } + } + + useEffect(() => { + const init = async () => { + const { + formCannotEditProps: setFormCannotEditProps, + schema, + jsonContent, + editingJsonFile: setEditingJsonFile, + } = await callService('config', 'getInitData'); + formCannotEditProps.current = setFormCannotEditProps; + schemaDefaultValue.current = getSchemaDefaultValue(schema); + editingJsonFile.current = setEditingJsonFile; + uischema.current = getUISchema(setFormCannotEditProps); + setCurrentSchema(schema); + setJsonContent(jsonContent); + setLoading(false); + }; + init(); + }, []); + + return ( + <> + {loading ? ( + + ) : ( + + + + + + )} + + ); +}; + +const IntlJsonForm = () => { + return ( + + + + ); +}; + +export default IntlJsonForm; diff --git a/extensions/iceworks-config-helper/web/src/routes.ts b/extensions/iceworks-config-helper/web/src/routes.ts new file mode 100644 index 000000000..391a49b81 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/routes.ts @@ -0,0 +1,10 @@ +import JsonForm from '@/pages/JsonForm/index'; + +const routerConfig = [ + { + path: '/', + component: JsonForm, + }, +]; + +export default routerConfig; diff --git a/extensions/iceworks-config-helper/web/src/theme/ChangeProvider.tsx b/extensions/iceworks-config-helper/web/src/theme/ChangeProvider.tsx new file mode 100644 index 000000000..2aaea306a --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/ChangeProvider.tsx @@ -0,0 +1,42 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useState, useEffect, useContext } from 'react'; +import { Box } from '@alifd/next'; +import * as _ from 'lodash'; +import { configHelperProvider } from '../pages/JsonForm/index'; + +const DEFUALT_ITEM_COLOR = '#1e1e1e'; +const CHANGED_ITEM_COLOR = '#0d7c9f'; +const DEFAULT_ITEM_MARGIN = '0 2px'; +const CHANGED_ITEM_MARGIN = '10px 2px 0px 2px'; +const WIDTH = '2px'; + +const ChangeProvider = ({ fieldKey, children }) => { + const [siderStyle, setSiderStyle] = useState({ + backgroundColor: DEFUALT_ITEM_COLOR, + width: WIDTH, + margin: DEFAULT_ITEM_MARGIN, + }); + const { jsonContent, defaultSchema } = useContext(configHelperProvider); + useEffect(() => { + const currentValue = jsonContent[fieldKey]; + const defaultValue = defaultSchema[fieldKey]; + setSiderStyle( + // 侧边栏的颜色表示了这个属性是否被改动,这个改动是以默认值为基准的 + // 参考 https://code.visualstudio.com/docs/getstarted/settings + _.isEqual(currentValue, defaultValue) || + _.isEqual(currentValue, {}) || + ((currentValue === '' || currentValue === false) && defaultValue === undefined) || + currentValue === undefined + ? { backgroundColor: DEFUALT_ITEM_COLOR, width: WIDTH, margin: DEFAULT_ITEM_MARGIN } + : { backgroundColor: CHANGED_ITEM_COLOR, width: WIDTH, margin: CHANGED_ITEM_MARGIN } + ); + }, [jsonContent]); + + return ( + +
+
{React.Children.only(children)}
+ + ); +}; +export default ChangeProvider; diff --git a/extensions/iceworks-config-helper/web/src/theme/DescriptionField.tsx b/extensions/iceworks-config-helper/web/src/theme/DescriptionField.tsx new file mode 100644 index 000000000..31034d991 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/DescriptionField.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { FieldProps } from '@rjsf/core'; + +const DescriptionField = ({ title }: FieldProps) => ( + <> +

{title}

+ +); + +export default DescriptionField; diff --git a/extensions/iceworks-config-helper/web/src/theme/EditInFile.tsx b/extensions/iceworks-config-helper/web/src/theme/EditInFile.tsx new file mode 100644 index 000000000..f1af70ead --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/EditInFile.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { useIntl } from 'react-intl'; +import callService from '../callService'; +import { configHelperProvider } from '../pages/JsonForm/index'; + +const EditInFile = ({ name }) => { + const { editingJsonFile } = React.useContext(configHelperProvider); + const intl = useIntl(); + const { defaultSchema } = React.useContext(configHelperProvider); + const sendDefaultValue = () => { + const editInJsonArgs = {}; + editInJsonArgs[name] = defaultSchema[name]; + callService('config', 'editInJsonFile', editInJsonArgs); + }; + return ( + + {intl.formatMessage({ id: 'web.iceworksConfigHelper.editInJsonFile.editInJsonLink' }, { editingJsonFile })} + + ); +}; +export default EditInFile; diff --git a/extensions/iceworks-config-helper/web/src/theme/FieldTemplate.tsx b/extensions/iceworks-config-helper/web/src/theme/FieldTemplate.tsx new file mode 100644 index 000000000..c4cdb2a43 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/FieldTemplate.tsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; +import { FieldTemplateProps } from '@rjsf/core'; +import { List } from '@alifd/next'; +import ChangeProvider from './ChangeProvider'; + +const INPUT_STYLE = { marginBottom: 15, color: 'white' }; +const FieldTemplate = ({ + id, + children, + rawErrors = [], + rawHelp, + label, + rawDescription, + schema, +}: FieldTemplateProps) => { + return ( + +
+

{label}

+ + {schema.type === 'boolean' ? null :

{rawDescription}

} + {children} + {rawErrors.length > 0 && ( + + {rawErrors.map((e) => ( + {e} + ))} + + )} + {rawHelp &&

{rawHelp}

} +
+
+ ); +}; + +export default FieldTemplate; diff --git a/extensions/iceworks-config-helper/web/src/theme/ObjectFieldTemplate.tsx b/extensions/iceworks-config-helper/web/src/theme/ObjectFieldTemplate.tsx new file mode 100644 index 000000000..872366da6 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/ObjectFieldTemplate.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { ObjectFieldTemplateProps } from '@rjsf/core'; + +const OBJECT_FILED_STYLE = { marginLeft: '6px' }; + +const ObjectFieldTemplate = ({ properties }: ObjectFieldTemplateProps) => { + return
{properties.map((element) => element.content)}
; +}; + +export default ObjectFieldTemplate; diff --git a/extensions/iceworks-config-helper/web/src/theme/TitleFiled.tsx b/extensions/iceworks-config-helper/web/src/theme/TitleFiled.tsx new file mode 100644 index 000000000..60c8f459c --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/TitleFiled.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { FieldProps } from '@rjsf/core'; + +const TitleField = ({ title }: FieldProps) => ( +

+ {title} +

+); + +export default TitleField; diff --git a/extensions/iceworks-config-helper/web/src/theme/checkBox.tsx b/extensions/iceworks-config-helper/web/src/theme/checkBox.tsx new file mode 100644 index 000000000..28ce95ddf --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/checkBox.tsx @@ -0,0 +1,24 @@ +import React, { useState, useEffect } from 'react'; +import { Checkbox } from '@alifd/next'; +import { WidgetProps } from '@rjsf/core'; + +const Fdcheckbox: React.FC = (props) => { + const { value, schema, onChange } = props; + const [checkedValue, setCheckedValue] = useState(value); + + const check = (checked) => { + onChange(checked); + setCheckedValue(checked); + }; + + useEffect(() => { + check(value); + }, [value]); + + return ( + check(checked)}> + {schema.description} + + ); +}; +export default Fdcheckbox; diff --git a/extensions/iceworks-config-helper/web/src/theme/fdSelectWidge.tsx b/extensions/iceworks-config-helper/web/src/theme/fdSelectWidge.tsx new file mode 100644 index 000000000..03b2cbde6 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/fdSelectWidge.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { WidgetProps } from '@rjsf/core'; +import { Select } from '@alifd/next'; + +const SelectWidget = ({ id, options, disabled, readonly, value, onChange, onBlur, onFocus }: WidgetProps) => { + const fdonChange = (e) => { + onChange(e); + }; + const fdonBlur = (e) => onBlur(id, e.target.value); + + const fdonFocus = (e) => onFocus(id, e.target.value); + + return ( + + ); +}; +export default FdTextWeight; diff --git a/extensions/iceworks-config-helper/web/src/theme/styles.css b/extensions/iceworks-config-helper/web/src/theme/styles.css new file mode 100644 index 000000000..3b8a8b3f9 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/styles.css @@ -0,0 +1,4 @@ +.fdInput { + background: #3c3c3c; + border-radius: 4px; +} diff --git a/extensions/iceworks-config-helper/web/src/theme/theme.tsx b/extensions/iceworks-config-helper/web/src/theme/theme.tsx new file mode 100644 index 000000000..48001e57b --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/theme/theme.tsx @@ -0,0 +1,25 @@ +import fdCheckBox from './checkBox'; +import fdEditInFile from './EditInFile'; +import fdTextInput from './fdTextInput'; +import titleFiled from './TitleFiled'; +import descriptionField from './DescriptionField'; +import FiledTemplate from './FieldTemplate'; +import ObjectFieldTemplate from './ObjectFieldTemplate'; +import selectWidget from './fdSelectWidge'; + +export const fields = { + TitleFiled: titleFiled, + DescriptionField: descriptionField, + ArrayField: fdEditInFile, + EditInFile: fdEditInFile, +}; + +export const widgets = { + CheckboxWidget: fdCheckBox, + TextWidget: fdTextInput, + SelectWidget: selectWidget, +}; +export const templates = { + FiledTemplate, + ObjectFieldTemplate, +}; diff --git a/extensions/iceworks-config-helper/web/src/typings.d.ts b/extensions/iceworks-config-helper/web/src/typings.d.ts new file mode 100644 index 000000000..0506fbcb4 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/typings.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.scss' { + const classes: { [key: string]: string }; + export default classes; +} diff --git a/extensions/iceworks-config-helper/web/src/utils.ts b/extensions/iceworks-config-helper/web/src/utils.ts new file mode 100644 index 000000000..66820e419 --- /dev/null +++ b/extensions/iceworks-config-helper/web/src/utils.ts @@ -0,0 +1,46 @@ +import * as _ from 'lodash'; + +export const getSchemaDefaultValue = (schema) => { + const DefaultSchema = {}; + _.forIn(schema.properties, (value, key) => { + DefaultSchema[key] = value.default; + }); + return DefaultSchema; +}; + +export function createIncremetalUpdate(newData, lastSyncJson, schemaDefaultValue, formCannotEditProps) { + const incrementalChange = {}; + const newSyncJson = { ...lastSyncJson }; + _.forIn(lastSyncJson, (value, key) => { + // 如果老数据中的值与新数据的值不相等,则意味着发生了变更,记录更新,使用新值 + if (!formCannotEditProps.includes(key) && !_.isEqual(value, newData[key])) { + incrementalChange[key] = newData[key]; + newSyncJson[key] = newData[key]; + } + }); + _.forIn(newData, (value, key) => { + if ([...formCannotEditProps, ...Object.keys(lastSyncJson)].includes(key)) { + // change nothing + } else if (schemaDefaultValue[key] !== undefined) { + // 如果 schema 中有默认值 + // 1. 新数据的值不等于默认值,意味着新增了数据,记录此数据 + // 2. 新数据的值等于默认值,则丢弃此值,不记录 + if (!_.isEqual(newData[key], schemaDefaultValue[key])) { + incrementalChange[key] = value; + newSyncJson[key] = value; + } + } else { + incrementalChange[key] = value; + newSyncJson[key] = value; + } + }); + return { incrementalChange, newSyncJson }; +} + +export const getUISchema = (formCannotEditProps) => { + const uiSchema = {}; + formCannotEditProps.forEach((prop) => { + uiSchema[prop] = { 'ui:field': 'EditInFile' }; + }); + return uiSchema; +}; diff --git a/extensions/iceworks-config-helper/web/tsconfig.json b/extensions/iceworks-config-helper/web/tsconfig.json new file mode 100644 index 000000000..0c52d50ba --- /dev/null +++ b/extensions/iceworks-config-helper/web/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compileOnSave": false, + "buildOnSave": false, + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "module": "esnext", + "target": "es6", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "rootDir": "./", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": false, + "importHelpers": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true, + "skipLibCheck": true, + "paths": { + "@/*": ["./src/*"], + "ice": [".ice/index.ts"], + "ice/*": [".ice/pages/*"] + }, + "resolveJsonModule": true + }, + "include": ["src", ".ice", "utils.ts"], + "exclude": ["node_modules", "build", "public"] +} diff --git a/extensions/iceworks-config-helper/webpack.config.js b/extensions/iceworks-config-helper/webpack.config.js new file mode 100644 index 000000000..4a17ef74f --- /dev/null +++ b/extensions/iceworks-config-helper/webpack.config.js @@ -0,0 +1,43 @@ +const path = require('path'); + +const tsConfigPath = path.join(__dirname, 'tsconfig.json'); +const config = { + target: 'node', + entry: './src/extension.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-project-creator/CHANGELOG.md b/extensions/iceworks-project-creator/CHANGELOG.md index eef0d2070..7aa8e7e74 100644 --- a/extensions/iceworks-project-creator/CHANGELOG.md +++ b/extensions/iceworks-project-creator/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 0.3.5 + +- feat: support create Mini Program App + ## 0.3.4 - fix: build error diff --git a/extensions/iceworks-project-creator/package.json b/extensions/iceworks-project-creator/package.json index 17fcf5c3a..d55c5be51 100644 --- a/extensions/iceworks-project-creator/package.json +++ b/extensions/iceworks-project-creator/package.json @@ -3,7 +3,7 @@ "displayName": "Iceworks Application Creator", "description": "Quick create a Universal Application(React/Rax/Vue, etc).", "publisher": "iceworks-team", - "version": "0.3.4", + "version": "0.3.5", "engines": { "vscode": "^1.41.0" }, diff --git a/extensions/iceworks-project-creator/web/src/components/ScaffoldCard/index.module.scss b/extensions/iceworks-project-creator/web/src/components/Card/index.module.scss similarity index 92% rename from extensions/iceworks-project-creator/web/src/components/ScaffoldCard/index.module.scss rename to extensions/iceworks-project-creator/web/src/components/Card/index.module.scss index 723eebeaf..994d81a7f 100644 --- a/extensions/iceworks-project-creator/web/src/components/ScaffoldCard/index.module.scss +++ b/extensions/iceworks-project-creator/web/src/components/Card/index.module.scss @@ -19,6 +19,9 @@ padding-bottom: 10px; margin-top: 10px; } + .next-card-header { + padding: 0 10px; + } } .content { diff --git a/extensions/iceworks-project-creator/web/src/components/ScaffoldCard/index.tsx b/extensions/iceworks-project-creator/web/src/components/Card/index.tsx similarity index 100% rename from extensions/iceworks-project-creator/web/src/components/ScaffoldCard/index.tsx rename to extensions/iceworks-project-creator/web/src/components/Card/index.tsx diff --git a/extensions/iceworks-project-creator/web/src/components/MobileScaffoldCard/index.module.scss b/extensions/iceworks-project-creator/web/src/components/MobileScaffoldCard/index.module.scss index 12dbdce32..5f54a5ea9 100644 --- a/extensions/iceworks-project-creator/web/src/components/MobileScaffoldCard/index.module.scss +++ b/extensions/iceworks-project-creator/web/src/components/MobileScaffoldCard/index.module.scss @@ -15,11 +15,12 @@ margin: 0 10px 20px 10px; cursor: pointer; position: relative; - background-color: white; :global { .next-card-media { height: 230px; + overflow: hidden; + img { max-height: none; } @@ -71,5 +72,5 @@ } .active { - border: 1px solid $color-notice-3; + border: 2px solid $color-notice-3; } diff --git a/extensions/iceworks-project-creator/web/src/constant.ts b/extensions/iceworks-project-creator/web/src/constant.ts index 5cdf2a8a2..f2399ecf1 100644 --- a/extensions/iceworks-project-creator/web/src/constant.ts +++ b/extensions/iceworks-project-creator/web/src/constant.ts @@ -4,6 +4,9 @@ export const mainScaffoldsList = [ '@alifd/scaffold-simple', '@rax-materials/scaffolds-app-js', '@rax-materials/scaffolds-app-ts', + '@miniprogram-materials/scaffolds-app-js', + '@miniprogram-materials/scaffolds-app-ts', + '@miniprogram-materials/scaffolds-todos', ]; export const tsScaffoldsList = [ @@ -13,10 +16,13 @@ export const tsScaffoldsList = [ '@icedesign/stark-layout-scaffold', '@icedesign/stark-child-scaffold', '@rax-materials/scaffolds-app-ts', + '@miniprogram-materials/scaffolds-app-ts', ]; export const jsScaffoldsList = [ '@alifd/fusion-design-pro-js', '@alifd/scaffold-lite-js', '@rax-materials/scaffolds-app-js', + '@miniprogram-materials/scaffolds-app-js', + '@miniprogram-materials/scaffolds-todos', ]; diff --git a/extensions/iceworks-project-creator/web/src/locales/en-US.json b/extensions/iceworks-project-creator/web/src/locales/en-US.json index 6ed64fe54..93aa783aa 100644 --- a/extensions/iceworks-project-creator/web/src/locales/en-US.json +++ b/extensions/iceworks-project-creator/web/src/locales/en-US.json @@ -26,5 +26,9 @@ "web.iceworksProjectCreator.CreateProjectForm.storage": "Local Path", "web.iceworksProjectCreator.CreateProjectForm.chooseLocalPath": "Choose Local Path for Application", "web.iceworksProjectCreator.ScaffoldMarket.noTemplate": "There is no template, comming soon...", - "web.iceworksProjectCreator.ScaffoldMarket.more": "More" + "web.iceworksProjectCreator.ScaffoldMarket.more": "More", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.chooseTargetPrompt": "Please choose at least one target", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.targetTitle": "Target(please choose at least one)", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.webAppTitle": "Choose Web App type", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.miniAppTitle": "Choose Mini App type" } diff --git a/extensions/iceworks-project-creator/web/src/locales/zh-CN.json b/extensions/iceworks-project-creator/web/src/locales/zh-CN.json index 2d46d779b..2ae1198b7 100644 --- a/extensions/iceworks-project-creator/web/src/locales/zh-CN.json +++ b/extensions/iceworks-project-creator/web/src/locales/zh-CN.json @@ -26,5 +26,9 @@ "web.iceworksProjectCreator.CreateProjectForm.storage": "本地路径", "web.iceworksProjectCreator.CreateProjectForm.chooseLocalPath": "请选择应用存储的本地路径", "web.iceworksProjectCreator.ScaffoldMarket.noTemplate": "暂无模板", - "web.iceworksProjectCreator.ScaffoldMarket.more": "查看更多" + "web.iceworksProjectCreator.ScaffoldMarket.more": "查看更多", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.chooseTargetPrompt": "请至少选择一个 Target", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.targetTitle": "Target(请至少选择一个)", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.webAppTitle": "为 Web 应用选择应用类型", + "web.iceworksProjectCreator.RaxScaffoldTypeForm.miniAppTitle": "为小程序选择构建类型" } diff --git a/extensions/iceworks-project-creator/web/src/pages/CreateProject/components/RaxScaffoldTypeForm/index.tsx b/extensions/iceworks-project-creator/web/src/pages/CreateProject/components/RaxScaffoldTypeForm/index.tsx index bfe01ddb0..1d4279136 100644 --- a/extensions/iceworks-project-creator/web/src/pages/CreateProject/components/RaxScaffoldTypeForm/index.tsx +++ b/extensions/iceworks-project-creator/web/src/pages/CreateProject/components/RaxScaffoldTypeForm/index.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import { Notification, Balloon } from '@alifd/next'; +import { useIntl } from 'react-intl'; import MenuCard from '@/components/MenuCard'; import { IProjectField } from '@/types'; import { targets, webAppTypes, miniAppTypes } from './config'; @@ -11,6 +12,7 @@ interface IScaffoldTypeForm { } const RaxScaffoldTypeForm: React.FC = ({ value, onChange }) => { + const intl = useIntl(); const [selectedTargets, setSelectedTargets] = useState(() => { if (value.ejsOptions && value.ejsOptions.targets && value.ejsOptions.targets instanceof Array) { return value.ejsOptions.targets; @@ -38,7 +40,9 @@ const RaxScaffoldTypeForm: React.FC = ({ value, onChange }) = const targetIndex = selectedTargets.findIndex((item) => target.type === item); if (targetIndex > -1) { if (selectedTargets.length === 1) { - Notification.error({ content: '请至少选择一个 Target' }); + Notification.error({ + content: intl.formatMessage({ id: 'web.iceworksProjectCreator.RaxScaffoldTypeForm.chooseTargetPrompt' }), + }); return; } // 删除已有的 target @@ -93,7 +97,9 @@ const RaxScaffoldTypeForm: React.FC = ({ value, onChange }) = }, []); return (
-
Target (至少选择一个)
+
+ {intl.formatMessage({ id: 'web.iceworksProjectCreator.RaxScaffoldTypeForm.targetTitle' })} +
{targets.map((item) => { const selected = selectedTargets.some((selectedTarget) => selectedTarget === item.type); @@ -119,7 +125,9 @@ const RaxScaffoldTypeForm: React.FC = ({ value, onChange }) =
{selectedTargets.some((item) => item === 'web') && ( <> -
为 Web 应用选择应用类型
+
+ {intl.formatMessage({ id: 'web.iceworksProjectCreator.RaxScaffoldTypeForm.webAppTitle' })} +
{webAppTypes.map((item) => ( = ({ value, onChange }) = )} {selectedTargets.some((item) => item === 'miniapp' || item === 'wechat-miniprogram' || item === 'kraken') && ( <> -
为小程序选择构建类型
+
+ {intl.formatMessage({ id: 'web.iceworksProjectCreator.RaxScaffoldTypeForm.miniAppTitle' })} +
{miniAppTypes.map((item) => ( { const intl = useIntl(); const [selectedSource, setSelectedSource] = useState({}); @@ -98,7 +102,7 @@ const ScaffoldMarket = ({ onScaffoldSelect, curProjectField, children, onOpenCon iconName = projectType; } return ( - @@ -136,8 +140,8 @@ const ScaffoldMarket = ({ onScaffoldSelect, curProjectField, children, onOpenCon : jsScaffoldsList.includes(item.source.npm) ? 'js' : ''; - const isRax = selectedSource.type === 'rax'; - const CardComponent = isRax ? MobileScaffoldCard : ScaffoldCard; + const isWireless = checkIsWireless(selectedSource); + const CardComponent = isWireless ? MobileScaffoldCard : Card; return ( { - let clones: IClone[] = []; - let repetitionPercentage = 0; + // let clones: IClone[] = []; + // let repetitionPercentage = 0; - try { - clones = await jscpd([ - '--formats-exts', - supportExts.join(','), - directory, - '--ignore', - `"${ignoreDirs.map((ignoreDir) => `${path.join(directory, '/')}**/${ignoreDir}/**`).join(',')}"`, - '--reporters', - 'json', - '--output', - tempDir, - '--silent', - ]); + // try { + // clones = await jscpd([ + // '--formats-exts', + // supportExts.join(','), + // directory, + // '--ignore', + // `"${ignoreDirs.map((ignoreDir) => `${path.join(directory, '/')}**/${ignoreDir}/**`).join(',')}"`, + // '--reporters', + // 'json', + // '--output', + // tempDir, + // '--silent', + // ]); - const repeatabilityResultFile = path.join(tempDir, 'jscpd-report.json'); - if (fs.existsSync(repeatabilityResultFile)) { - const repeatabilityResult = fs.readJSONSync(repeatabilityResultFile); - repetitionPercentage = repeatabilityResult.statistics.total.percentage; - } - } catch (e) { - // ignore - console.log(e); - } + // const repeatabilityResultFile = path.join(tempDir, 'jscpd-report.json'); + // if (fs.existsSync(repeatabilityResultFile)) { + // const repeatabilityResult = fs.readJSONSync(repeatabilityResultFile); + // repetitionPercentage = repeatabilityResult.statistics.total.percentage; + // } + // } catch (e) { + // // ignore + // console.log(e); + // } return { - score: new Scorer().minus(repetitionPercentage), - clones, + score: 0, + clones: [], }; } diff --git a/packages/eslint-plugin-security-practices/README.md b/packages/eslint-plugin-security-practices/README.md new file mode 100644 index 000000000..e22419fcf --- /dev/null +++ b/packages/eslint-plugin-security-practices/README.md @@ -0,0 +1,57 @@ +# @iceworks/eslint-plugin-security-practices + +Iceworks doctor security practices eslint plugin. + +## Installation + +Install [esLint](http://eslint.org), `@ice/spec` and `@iceworks/eslint-plugin-security-practices`: + +```shell +$ npm install --save-dev eslint @ice/spec @iceworks/eslint-plugin-security-practices +``` + +## Usage + +Add `security-practices` to the plugins section of your `.eslintrc` configuration file. + +```json +{ + "plugins": [ + "@iceworks/security-practices" + ] +} +``` + +Then configure the rules you want to use under the rules section. + +```json +{ + "plugins": [ + "@iceworks/security-practices" + ], + "rules": { + "@iceworks/security-practices/rule-name": 2 + } +} +``` + +Or you can only use extends to set default rules config. + +```json +{ + "extends": ["plugin:@iceworks/security-practices/recommended"] +}; +``` + +## Supported Rules + +* [`no-http-url`](./docs/rules/no-http-url.md) Recommended the http url switch to HTTPS. +* [`no-internal-url`](./docs/rules/no-internal-url.md) The group internal url is not recommended. +* [`no-patent-licenses`](./docs/rules/no-patent-licenses.md) Recommend to use the dependency with Open-Source license. +* [`no-secret-info`](./docs/rules/no-secret-info.md) Don't show `password` `token` and `secret` in plain text. +* [`no-sensitive-word`](./docs/rules/no-sensitive-word.md) Don't use some sensitive word. + + + + + diff --git a/packages/eslint-plugin-security-practices/docs/rules/no-http-url.md b/packages/eslint-plugin-security-practices/docs/rules/no-http-url.md new file mode 100644 index 000000000..c04a238d6 --- /dev/null +++ b/packages/eslint-plugin-security-practices/docs/rules/no-http-url.md @@ -0,0 +1,16 @@ +# no-http-url + +Recommended the http url switch to HTTPS. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +var test = 'http://test.com'; +var jsx = ; +``` + +## When Not To Use It + +If your website only support http. diff --git a/packages/eslint-plugin-security-practices/docs/rules/no-internal-url.md b/packages/eslint-plugin-security-practices/docs/rules/no-internal-url.md new file mode 100644 index 000000000..f5d95c642 --- /dev/null +++ b/packages/eslint-plugin-security-practices/docs/rules/no-internal-url.md @@ -0,0 +1,24 @@ +# Internal url is not recommended (no-internal-url) + +The group internal url is not recommended. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +request('yourSite.com'); + +``` + +### Options + +Group internal url list。 + +```json +{ + "rules": { + "@iceworks/security-practices/no-internal-url": ["warn", ["yourSite.com"]] + }, +} +``` diff --git a/packages/eslint-plugin-security-practices/docs/rules/no-patent-licenses.md b/packages/eslint-plugin-security-practices/docs/rules/no-patent-licenses.md new file mode 100644 index 000000000..988c23fb1 --- /dev/null +++ b/packages/eslint-plugin-security-practices/docs/rules/no-patent-licenses.md @@ -0,0 +1,9 @@ +# no-patent-licenses + + Recommend to use the dependency with Open-Source license. + +## Rule Details + +See [https://spdx.org/licenses/](https://spdx.org/licenses/). + +The package with Patent(like 'BSD-2-Clause-Patent') will be warned. diff --git a/packages/eslint-plugin-security-practices/docs/rules/no-secret-info.md b/packages/eslint-plugin-security-practices/docs/rules/no-secret-info.md new file mode 100644 index 000000000..837c696cd --- /dev/null +++ b/packages/eslint-plugin-security-practices/docs/rules/no-secret-info.md @@ -0,0 +1,31 @@ +# no-secret-info + +Don't show `password` `token` and `secret` in plain text. + + +## Rule Details + +It is very dangerous to use plain text with key contains `password` `token` and `secret`. + +Examples of **incorrect** code for this rule: + +```js + +var accessKeySecret = 'xxxx'; + +var client ={ + accessKeyToken: 'xxxx' +}; +``` + +Examples of **correct** code for this rule: + +```js + +var accessKeySecret = process.env.ACCESS_KEY_SECRET; + +var client ={ + accessKeyToken: process.env.ACCESS_KEY_SECRET +}; + +``` diff --git a/packages/eslint-plugin-security-practices/docs/rules/no-sensitive-word.md b/packages/eslint-plugin-security-practices/docs/rules/no-sensitive-word.md new file mode 100644 index 000000000..271635cc5 --- /dev/null +++ b/packages/eslint-plugin-security-practices/docs/rules/no-sensitive-word.md @@ -0,0 +1,26 @@ +# no-sensitive-word + +Don't use some sensitive word. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js + +// stupid xxx +const jsx =

汇款

; + +``` + +### Options + +Sensitive word list。 + +```json +{ + "rules": { + "@iceworks/security-practices/no-sensitive-word": ["warn", ["fuck", "汇款"]], + }, +} + diff --git a/packages/eslint-plugin-security-practices/package.json b/packages/eslint-plugin-security-practices/package.json new file mode 100644 index 000000000..1bb49a234 --- /dev/null +++ b/packages/eslint-plugin-security-practices/package.json @@ -0,0 +1,34 @@ +{ + "name": "@iceworks/eslint-plugin-security-practices", + "version": "0.1.0", + "description": "Iceworks doctor security practices eslint plugin.", + "files": [ + "docs/", + "lib/" + ], + "keywords": [ + "iceworks", + "doctor", + "security", + "eslint", + "eslintplugin", + "eslint-plugin" + ], + "main": "lib/index.js", + "publishConfig": { + "access": "public" + }, + "scripts": { + "test": "mocha tests --recursive" + }, + "dependencies": { + "@ice/spec": "^1.0.1", + "fastscan": "^1.0.6", + "line-column": "^1.0.2", + "requireindex": "~1.1.0" + }, + "devDependencies": { + "eslint": "^7.1.0", + "mocha": "^7.2.0" + } +} diff --git a/packages/eslint-plugin-security-practices/src/configs/recommended.ts b/packages/eslint-plugin-security-practices/src/configs/recommended.ts new file mode 100644 index 000000000..f7fcacba2 --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/configs/recommended.ts @@ -0,0 +1,29 @@ +import { eslint, deepmerge } from '@ice/spec'; + +const defaultEslintConfig = Object.assign({}, eslint); + +if (!defaultEslintConfig.overrides) { + defaultEslintConfig.overrides = []; +} + +// Recommended security practices eslint config +module.exports = deepmerge(defaultEslintConfig, { + plugins: ['@iceworks/security-practices'], + overrides: [ + { + files: ['package.json'], + processor: '@iceworks/security-practices/.json', + rules: { + quotes: 'off', + '@iceworks/security-practices/no-patent-licenses': 'warn', + }, + }, + ], + rules: { + 'react/react-in-jsx-scope': 'off', // For rax + '@iceworks/security-practices/no-http-url': 'warn', + '@iceworks/security-practices/no-internal-url': ['warn', ['taobao.net']], + '@iceworks/security-practices/no-secret-info': 'error', + '@iceworks/security-practices/no-sensitive-word': ['warn', ['fuck']], + }, +}); diff --git a/packages/eslint-plugin-security-practices/src/docsUrl.ts b/packages/eslint-plugin-security-practices/src/docsUrl.ts new file mode 100644 index 000000000..e2ea5aac5 --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/docsUrl.ts @@ -0,0 +1,5 @@ +const repoUrl = 'https://github.com/ice-lab/iceworks/tree/master/packages/eslint-plugin-security-practices'; + +export default function docsUrl(ruleName: string) { + return `${repoUrl}/docs/rules/${ruleName}.md`; +} diff --git a/packages/eslint-plugin-security-practices/src/index.ts b/packages/eslint-plugin-security-practices/src/index.ts new file mode 100644 index 000000000..7fd55ed0e --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/index.ts @@ -0,0 +1,13 @@ +import * as requireIndex from 'requireindex'; + +export const rules = requireIndex(`${__dirname}/rules`); +export const configs = requireIndex(`${__dirname}/configs`); + +export const processors = { + '.json': { + preprocess(text: string) { + // As JS file + return [`module.exports = ${text}`]; + }, + }, +}; diff --git a/packages/eslint-plugin-security-practices/src/rules/no-http-url.ts b/packages/eslint-plugin-security-practices/src/rules/no-http-url.ts new file mode 100644 index 000000000..0cd8ecc2e --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/rules/no-http-url.ts @@ -0,0 +1,36 @@ +import docsUrl from '../docsUrl'; + +const RULE_NAME = 'no-http-url'; + +module.exports = { + name: RULE_NAME, + meta: { + type: 'suggestion', + docs: { + url: docsUrl(RULE_NAME), + }, + fixable: 'code', + messages: { + // eslint-disable-next-line + noHttpUrl: "Recommended '{{url}}' switch to HTTPS", + }, + }, + create(context: any) { + return { + Literal: function handleRequires(node: any) { + if (node.value && node.value.indexOf('http:') === 0) { + context.report({ + node, + messageId: 'noHttpUrl', + data: { + url: node.value, + }, + fix: (fixer: any) => { + return fixer.replaceText(node, `'${node.value.replace('http:', 'https:')}'`); + }, + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-security-practices/src/rules/no-internal-url.ts b/packages/eslint-plugin-security-practices/src/rules/no-internal-url.ts new file mode 100644 index 000000000..86519bb54 --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/rules/no-internal-url.ts @@ -0,0 +1,45 @@ +import docsUrl from '../docsUrl'; + +const RULE_NAME = 'no-internal-url'; + +const defaultUrlList = ['alibaba-inc.com']; + +module.exports = { + name: RULE_NAME, + meta: { + type: 'suggestion', + docs: { + url: docsUrl(RULE_NAME), + }, + fixable: null, + schema: [ + { + type: 'array', + items: { + type: 'string', + }, + }, + ], + messages: { + // eslint-disable-next-line + noInternalUrl: "The url '{{url}}' is not recommended", + }, + }, + + create(context) { + const reg = new RegExp(defaultUrlList.concat(context.options[0] || []).join('|')); + return { + Literal: function handleRequires(node: any) { + if (reg.test(node.value)) { + context.report({ + node, + messageId: 'noInternalUrl', + data: { + url: node.value, + }, + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-security-practices/src/rules/no-patent-licenses.ts b/packages/eslint-plugin-security-practices/src/rules/no-patent-licenses.ts new file mode 100644 index 000000000..6385ecc88 --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/rules/no-patent-licenses.ts @@ -0,0 +1,62 @@ +import * as path from 'path'; +import * as fs from 'fs-extra'; +import docsUrl from '../docsUrl'; + +const RULE_NAME = 'no-patent-licenses'; + +module.exports = { + name: RULE_NAME, + meta: { + type: 'suggestion', + docs: { + url: docsUrl(RULE_NAME), + }, + fixable: null, + messages: { + // eslint-disable-next-line + noPatentLicenses: "The package '{{dependencyName}}' with license '{{license}}' may cause some problem.", + }, + }, + + create(context) { + if (path.basename(context.getFilename()) !== 'package.json') { + return; + } + + const cwd = context.getCwd(); + + return { + Property: function handleRequires(node: any) { + if ( + node.key && + node.key.value && + (node.key.value === 'dependencies' || node.key.value === 'devDependencies') && + node.value && + node.value.properties + ) { + node.value.properties.forEach((property) => { + if (property.key && property.key.value) { + const dependencyName = property.key.value; + const dependencyPackageFile = path.join(cwd, 'node_modules', dependencyName, 'package.json'); + if (fs.existsSync(dependencyPackageFile)) { + const dependencyPackage = fs.readJSONSync(dependencyPackageFile); + const license = dependencyPackage.license || ''; + // https://spdx.org/licenses/ + if (license.indexOf('Patent') > -1) { + context.report({ + loc: property.loc, + messageId: 'noPatentLicenses', + data: { + dependencyName, + license, + }, + }); + } + } + } + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-security-practices/src/rules/no-secret-info.ts b/packages/eslint-plugin-security-practices/src/rules/no-secret-info.ts new file mode 100644 index 000000000..429727a98 --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/rules/no-secret-info.ts @@ -0,0 +1,49 @@ +import docsUrl from '../docsUrl'; + +const RULE_NAME = 'no-secret-info'; + +const DANGEROUS_KEYS = ['secret', 'token', 'password']; + +module.exports = { + meta: { + type: 'problem', + docs: { + url: docsUrl(RULE_NAME), + }, + fixable: null, + messages: { + // eslint-disable-next-line + noSecretInfo: "Detect that the '{{secret}}' might be a secret token, Please check!", + }, + }, + + create(context) { + const reg = new RegExp(DANGEROUS_KEYS.join('|')); + + return { + Literal: function handleRequires(node: any) { + if ( + (node.parent && + // var secret = 'test'; + node.parent.type === 'VariableDeclarator' && + node.parent.id && + node.parent.id.name && + reg.test(node.parent.id.name.toLocaleLowerCase())) || + // { secret: 'test' }; + (node.parent.type === 'Property' && + node.parent.key && + node.parent.key.name && + reg.test(node.parent.key.name.toLocaleLowerCase())) + ) { + context.report({ + node, + messageId: 'noSecretInfo', + data: { + secret: node.value, + }, + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-security-practices/src/rules/no-sensitive-word.ts b/packages/eslint-plugin-security-practices/src/rules/no-sensitive-word.ts new file mode 100644 index 000000000..524477ba5 --- /dev/null +++ b/packages/eslint-plugin-security-practices/src/rules/no-sensitive-word.ts @@ -0,0 +1,64 @@ +import * as FastScanner from 'fastscan'; +import * as lineColumn from 'line-column'; +import docsUrl from '../docsUrl'; + +const RULE_NAME = 'no-sensitive-word'; + +const defaultWordList = ['stupid', 'foolish', 'silly']; + +module.exports = { + name: RULE_NAME, + meta: { + type: 'suggestion', + docs: { + url: docsUrl(RULE_NAME), + }, + fixable: null, + schema: [ + { + type: 'array', + items: { + type: 'string', + }, + }, + ], + messages: { + // eslint-disable-next-line + noSensitiveWord: "Detect the sensitive word: '{{word}}'", + }, + }, + + create(context) { + const sourceCode = context.getSourceCode(); + const sourceCodeText = sourceCode.getText(); + + const wordList = defaultWordList.concat(context.options[0] || []); + const scanner = new FastScanner(wordList); + + scanner.search(sourceCodeText).forEach((wordInfo) => { + const [wordIndex, word] = wordInfo; + const startPositionInfo = lineColumn(sourceCodeText).fromIndex(wordIndex); + const endPositionInfo = lineColumn(sourceCodeText).fromIndex(wordIndex + word.length - 1); + + context.report({ + loc: { + start: { + line: startPositionInfo.line, + column: startPositionInfo.col, + }, + end: { + line: endPositionInfo.line, + column: endPositionInfo.col, + }, + }, + messageId: 'noSensitiveWord', + data: { + word, + }, + }); + }); + + // Necessary + return {}; + }, +}; diff --git a/packages/eslint-plugin-security-practices/tests/lib/rules/no-http-url.js b/packages/eslint-plugin-security-practices/tests/lib/rules/no-http-url.js new file mode 100644 index 000000000..aa53f3906 --- /dev/null +++ b/packages/eslint-plugin-security-practices/tests/lib/rules/no-http-url.js @@ -0,0 +1,32 @@ +"use strict"; + +var rule = require("../../../lib/rules/no-http-url"); +var RuleTester = require("eslint").RuleTester; + +var ruleTester = new RuleTester(); + +ruleTester.run("no-http-url", rule, { + + valid: [{ + code: "var test = 'https://test.com';" + }], + + invalid: [{ + code: "var test = 'http://test.com';", + output: "var test = 'https://test.com';", + errors: [{ + message: "Recommended 'http://test.com' switch to HTTPS" + }] + }, { + code: "", + output: "", + parserOptions: { + ecmaFeatures: { + jsx: true + } + }, + errors: [{ + message: "Recommended 'http://test.com' switch to HTTPS" + }] + }] +}); diff --git a/packages/eslint-plugin-security-practices/tests/lib/rules/no-internal-url.js b/packages/eslint-plugin-security-practices/tests/lib/rules/no-internal-url.js new file mode 100644 index 000000000..bdfb8e7a5 --- /dev/null +++ b/packages/eslint-plugin-security-practices/tests/lib/rules/no-internal-url.js @@ -0,0 +1,27 @@ +"use strict"; + +var rule = require("../../../lib/rules/no-internal-url"); +var RuleTester = require("eslint").RuleTester; + +var ruleTester = new RuleTester(); + +ruleTester.run("no-internal-url", rule, { + + valid: [{ + code: "var test = 'https://test.com';" + }], + + invalid: [{ + code: "var test = 'https://test.com';", + options: [['test.com']], + errors: [{ + message: "The url 'https://test.com' is not recommended" + }] + }, + { + code: "var test = 'https://test.alibaba-inc.com';", + errors: [{ + message: "The url 'https://test.alibaba-inc.com' is not recommended" + }] + }] +}); diff --git a/packages/eslint-plugin-security-practices/tests/lib/rules/no-secret-info.js b/packages/eslint-plugin-security-practices/tests/lib/rules/no-secret-info.js new file mode 100644 index 000000000..553122a9f --- /dev/null +++ b/packages/eslint-plugin-security-practices/tests/lib/rules/no-secret-info.js @@ -0,0 +1,34 @@ +"use strict"; + +var rule = require("../../../lib/rules/no-secret-info"); +var RuleTester = require("eslint").RuleTester; + +var ruleTester = new RuleTester(); + +ruleTester.run("no-secret-info", rule, { + valid: [{ + code: "var accessKeySecret = process.env.ACCESS_KEY_SECRET;" + },{ + code: ` + var client ={ + accessKeyToken: process.env.ACCESS_KEY_SECRET + }; + ` + }], + + invalid: [{ + code: "var accessKeySecret = 'xxxx';", + errors: [{ + message: "Detect that the 'xxxx' might be a secret token, Please check!" + }] + }, { + code: ` + var client ={ + accessKeyToken: 'xxxx' + }; + `, + errors: [{ + message: "Detect that the 'xxxx' might be a secret token, Please check!" + }] + }] +}); diff --git a/packages/eslint-plugin-security-practices/tests/lib/rules/no-sensitive-word.js b/packages/eslint-plugin-security-practices/tests/lib/rules/no-sensitive-word.js new file mode 100644 index 000000000..edbdad680 --- /dev/null +++ b/packages/eslint-plugin-security-practices/tests/lib/rules/no-sensitive-word.js @@ -0,0 +1,21 @@ +"use strict"; + +var rule = require("../../../lib/rules/no-sensitive-word"); +var RuleTester = require("eslint").RuleTester; + +var ruleTester = new RuleTester(); + +ruleTester.run("no-sensitive-word", rule, { + + valid: [{ + code: "// nice" + }], + + invalid: [{ + code: "// fuck", + options: [['fuck']], + errors: [{ + message: "Detect the sensitive word: 'fuck'" + }] + }] +}); diff --git a/packages/eslint-plugin-security-practices/tsconfig.json b/packages/eslint-plugin-security-practices/tsconfig.json new file mode 100644 index 000000000..f2414a4b5 --- /dev/null +++ b/packages/eslint-plugin-security-practices/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.settings.json", + "compilerOptions": { + "baseUrl": "./", + "rootDir": "src", + "outDir": "lib" + }, + "exclude": ["tests", "lib"] +} diff --git a/packages/material-service/package.json b/packages/material-service/package.json index 2685b12bf..d2d7def81 100644 --- a/packages/material-service/package.json +++ b/packages/material-service/package.json @@ -1,6 +1,6 @@ { "name": "@iceworks/material-service", - "version": "0.1.9", + "version": "0.1.10", "description": "Iceworks material service for VSCode extension.", "files": [ "lib" diff --git a/packages/material-service/src/index.ts b/packages/material-service/src/index.ts index e72637f39..be176e3a8 100644 --- a/packages/material-service/src/index.ts +++ b/packages/material-service/src/index.ts @@ -11,9 +11,14 @@ import { getProjectType } from '@iceworks/project-service'; import i18n from './i18n'; const ICE_MATERIAL_SOURCE = 'https://ice.alicdn.com/assets/materials/react-materials.json'; +const VUE_MATERIAL_SOURCE = 'https://ice.alicdn.com/assets/materials/vue-materials.json'; +const MINI_PROGRAM_MATERIAL_SOURCE = 'https://ice.alicdn.com/assets/materials/miniprogram-materials.json'; +const RAX_MATERIAL_SOURCE = 'https://ice.alicdn.com/assets/materials/rax-materials.json'; + const MATERIAL_BASE_HOME_URL = 'https://ice.work/component'; const MATERIAL_BASE_REPOSITORY_URL = 'https://github.com/alibaba-fusion/next/tree/master/src'; const ICE_BASE_COMPONENTS_SOURCE = 'https://ice.alicdn.com/assets/base-components-1.x.json'; + const OFFICAL_MATERIAL_SOURCES = [ { name: 'PC Web', @@ -22,19 +27,30 @@ const OFFICAL_MATERIAL_SOURCES = [ source: ICE_MATERIAL_SOURCE, description: i18n.format('package.materialService.index.webDescription'), }, +]; + +const OFFICAL_MATERIAL_SOURCES_FOR_INTERNAL = [ { name: i18n.format('package.materialService.index.raxTitle'), type: 'rax', client: 'wireless', - source: 'https://ice.alicdn.com/assets/materials/rax-materials.json', + source: RAX_MATERIAL_SOURCE, description: i18n.format('package.materialService.index.raxDescription'), }, ]; + const OFFICAL_MATERIAL_SOURCES_FOR_EXTERNAL = [ + { + name: i18n.format('package.materialService.index.miniProgramTitle'), + type: 'miniProgram', + client: 'wireless', + source: MINI_PROGRAM_MATERIAL_SOURCE, + description: i18n.format('package.materialService.index.miniProgramDescription'), + }, { name: i18n.format('package.materialService.index.vueTitle'), type: 'vue', - source: 'https://ice.alicdn.com/assets/materials/vue-materials.json', + source: VUE_MATERIAL_SOURCE, description: i18n.format('package.materialService.index.vueDescription'), }, ]; @@ -63,9 +79,10 @@ export async function getSources(specifiedType?: string): Promise type === specifiedType) : sources; diff --git a/packages/material-service/src/locales/en-US.json b/packages/material-service/src/locales/en-US.json index 82e66bac4..e115005fc 100644 --- a/packages/material-service/src/locales/en-US.json +++ b/packages/material-service/src/locales/en-US.json @@ -4,6 +4,8 @@ "package.materialService.index.raxDescription": "Offical Materials Based On Rax Components Rax CLI", "package.materialService.index.vueTitle": "Vue materials", "package.materialService.index.vueDescription": "Offical Materials Based On Element And Vue CLI ", + "package.materialService.index.miniProgramTitle": "Mini Program", + "package.materialService.index.miniProgramDescription": "This is an ice material project for Mini Program App", "package.materialService.index.materialSourceExistError": "Material source already exists.", "package.materialService.index.materialNameExistError": "Material name s already exists.", "package.materialService.index.materialDataError": "Incorrect material source " diff --git a/packages/material-service/src/locales/zh-CN.json b/packages/material-service/src/locales/zh-CN.json index 2fc43e8ce..750a10540 100644 --- a/packages/material-service/src/locales/zh-CN.json +++ b/packages/material-service/src/locales/zh-CN.json @@ -4,6 +4,8 @@ "package.materialService.index.raxDescription": "基于 Rax 组件和 Rax 脚手架的官方物料", "package.materialService.index.vueTitle": "Vue 物料源", "package.materialService.index.vueDescription": "基于 Element, Vue CLI 的 Vue 官方物料", + "package.materialService.index.miniProgramTitle": "小程序", + "package.materialService.index.miniProgramDescription": "基于 React 的小程序物料源", "package.materialService.index.materialSourceExistError": "物料源已存在。", "package.materialService.index.materialNameExistError": "物料源名称已存在。", "package.materialService.index.materialDataError": "物料源数据错误。" diff --git a/packages/project-service/package.json b/packages/project-service/package.json index f990b5ce5..fb6a03be9 100644 --- a/packages/project-service/package.json +++ b/packages/project-service/package.json @@ -1,6 +1,6 @@ { "name": "@iceworks/project-service", - "version": "0.1.15", + "version": "0.1.16", "description": "Iceworks project service for VSCode extension.", "files": [ "lib" diff --git a/packages/project-service/src/index.ts b/packages/project-service/src/index.ts index d193a3c6d..4f19410eb 100644 --- a/packages/project-service/src/index.ts +++ b/packages/project-service/src/index.ts @@ -40,17 +40,22 @@ export async function getProjectLanguageType() { } export async function getProjectType() { - const { dependencies = {} } = await readPackageJSON(projectPath); - if (dependencies.rax) { - return 'rax'; + try { + const { dependencies = {} } = await readPackageJSON(projectPath); + if (dependencies.rax) { + return 'rax'; + } + if (dependencies.react) { + return 'react'; + } + if (dependencies.vue) { + return 'vue'; + } + return 'unknown'; + } catch (e) { + console.error(e); + return 'unknown'; } - if (dependencies.react) { - return 'react'; - } - if (dependencies.vue) { - return 'vue'; - } - return 'unknown'; } export async function getProjectFramework() { diff --git a/tsconfig.json b/tsconfig.json index eb7f18e5c..9e82b80db 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "files": [], "references": [ { "path": "packages/doctor" }, + { "path": "packages/eslint-plugin-security-practices" }, { "path": "packages/constant" }, { "path": "packages/i18n" }, { "path": "packages/ice-npm-utils" },