Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
24d07c4
add status bar
huanguolin Apr 16, 2025
9f3b139
refactor for command register
huanguolin Apr 16, 2025
fe915bd
implement basic rpc communication via websocket
huanguolin Apr 19, 2025
3820fac
complete basic communication
huanguolin Apr 20, 2025
5f10c4a
fix not parse rpc result
huanguolin Apr 20, 2025
30d2b36
add reconnect control for rpc client
huanguolin Apr 21, 2025
11f3b18
refactor error handle of RpcRouter
huanguolin Apr 21, 2025
8efae25
complete api replace on rpc server side
huanguolin Apr 21, 2025
19c081e
fix require esm in cjs issue cause e2e test failed
huanguolin Apr 22, 2025
0837ed9
remove unused code and rename
huanguolin Apr 22, 2025
ba7bd4e
add log
huanguolin Apr 22, 2025
ac27ec6
improve log
huanguolin Apr 23, 2025
66f52f4
improve rpc server status
huanguolin Apr 23, 2025
a22f459
add trigger ts server
huanguolin Apr 23, 2025
8827953
remove unused codex
huanguolin Apr 24, 2025
0cc7f80
update for state control
huanguolin Apr 24, 2025
3ea0728
fix e2e test and add log record for ts rpc client
huanguolin Apr 26, 2025
dcc04a6
complete state control
huanguolin Apr 26, 2025
92d8fb0
fix eslint error
Jun 4, 2025
4bcd735
fix e2e test failed
Jun 4, 2025
b1b2b7d
update status bar display
Jun 5, 2025
7ff1fb4
add comment
Jun 5, 2025
f7cda6c
make websocket server run in an subprocess
huanguolin Jun 7, 2025
27e2392
fix rpc esbuild issue
huanguolin Jun 7, 2025
ca95cde
improve ng-helper-vscode side log
huanguolin Jun 7, 2025
9128bdf
increase rpc heartbeat interval from 5s -> 30s
huanguolin Jun 7, 2025
b4a7bdd
implement depth-first search (DFS) option to fix some case cause ng-h…
huanguolin Jun 7, 2025
995677d
fix file path need normalize on windows
Jun 9, 2025
7259e46
avoid duplicate project add
Jun 9, 2025
efc6ffa
add new config
huanguolin Jun 10, 2025
53f87cb
update
Jun 11, 2025
fc2fe5f
fix update config not working in some case
huanguolin Jun 11, 2025
685952a
fix e2e test failed
huanguolin Jun 11, 2025
5623692
update
huanguolin Jun 11, 2025
1377fd7
extract getNormalizedPathFromDocument()
Jun 12, 2025
06754ab
refactor config
Jun 12, 2025
ba507cf
ng-helper-vscode support working scope base on config
Jun 12, 2025
88fc4c2
add result for triggerTsServerByProject()
huanguolin Jun 12, 2025
c149ed6
upgrade ts from 5.4.5 to 5.5.4, and intro zod lib
huanguolin Jun 12, 2025
ba0af1e
impl parseAndValidateUserConfig()
huanguolin Jun 12, 2025
2ba9140
handle user config error
Jun 13, 2025
8d60126
improve user config validate
huanguolin Jun 13, 2025
eff6e27
fix config issue
huanguolin Jun 13, 2025
971db50
impl NgHelperConfig method
huanguolin Jun 13, 2025
397373b
change state control max loading time: 5s -> 10s
huanguolin Jun 14, 2025
f0e7468
improve status bar
huanguolin Jun 14, 2025
fe5cd05
improve status bar
huanguolin Jun 14, 2025
f8b9f61
remove TODO comment
huanguolin Jun 14, 2025
d616b6c
fix status bar item color issue
huanguolin Jun 14, 2025
618a7bc
improve loading control for stateControl
huanguolin Jun 14, 2025
ccd4e48
simple user config
huanguolin Jun 15, 2025
018032e
reduce. BASE_START_TIME
huanguolin Jun 15, 2025
af9c4ea
improve error message while read user config
Jun 16, 2025
0ebd2de
adjust status bar hover tips
Jun 16, 2025
fb64af6
watch user config and popup hint for user
Jun 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"label": "watch",
"dependsOn": [
"plugin:watch:esbuild",
"watch:esbuild"
"ext:watch:esbuild",
"rpc:watch:esbuild"
],
"presentation": {
"reveal": "never"
Expand All @@ -18,14 +19,32 @@
}
},
{
"label": "watch:esbuild",
"label": "ext:watch:esbuild",
"type": "shell",
"command": "yarn",
"args": [
"workspace",
"ng-helper",
"run",
"watch:esbuild"
"watch:esbuild:ext"
],
"group": "build",
"problemMatcher": "$esbuild-watch",
"isBackground": true,
"presentation": {
"group": "watch",
"reveal": "never"
}
},
{
"label": "rpc:watch:esbuild",
"type": "shell",
"command": "yarn",
"args": [
"workspace",
"ng-helper",
"run",
"watch:esbuild:rpc"
],
"group": "build",
"problemMatcher": "$esbuild-watch",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"eslint-plugin-unused-imports": "^3.2.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.2",
"typescript": "~5.4.5"
"typescript": "~5.5.4"
},
"engines": {
"node": ">=18.*"
Expand Down
16 changes: 11 additions & 5 deletions packages/ng-helper-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,12 @@
"plugin:compile": "cd ../typescript-plugin && yarn compile",
"plugin:compile:e2e": "cd ../typescript-plugin && yarn compile:e2e",
"plugin:clean": "node scripts/resolver.js --clean",
"compile": "yarn tsc && yarn lint && node esbuild.js --production",
"compile:e2e": "yarn plugin:compile:e2e && node esbuild.js",
"watch:esbuild": "yarn plugin:clean && node esbuild.js --watch",
"compile": "yarn compile:ext && yarn compile:rpc",
"compile:ext": "yarn tsc && yarn lint && node scripts/esbuild-ext.js --production",
"compile:rpc": "yarn tsc && yarn lint && node scripts/esbuild-rpc.js --production",
"compile:e2e": "yarn plugin:compile:e2e && node scripts/esbuild-ext.js && node scripts/esbuild-rpc.js",
"watch:esbuild:ext": "yarn plugin:clean && node scripts/esbuild-ext.js --watch",
"watch:esbuild:rpc": "node scripts/esbuild-rpc.js --watch",
"tsc": "tsc --noEmit",
"lint": "eslint src --ext ts",
"test:e2e:u": "cross-env UPDATE_SNAPSHOT=1 yarn test:e2e",
Expand All @@ -132,14 +135,17 @@
"axios": "^1.7.2",
"change-case": "^5.4.4",
"fuzzysort": "^3.0.2",
"get-port": "^6.1.2"
"get-port": "^6.1.2",
"ws": "^7.5.10",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/chai": "^5.0.1",
"@types/fs-extra": "^11.0.4",
"@types/mocha": "^10.0.9",
"@types/node": "18.x",
"@types/vscode": "^1.80.0",
"@types/ws": "^7.4.7",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.11.0",
"@vscode/test-electron": "^2.4.1",
Expand All @@ -154,6 +160,6 @@
"mocha": "^10.8.2",
"mocha-chai-jest-snapshot": "^1.1.6",
"npm-run-all": "^4.1.5",
"typescript": "~5.4.5"
"typescript": "~5.5.4"
}
}
56 changes: 56 additions & 0 deletions packages/ng-helper-vscode/scripts/esbuild-rpc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint-disable no-undef */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const esbuild = require('esbuild');

const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');

/**
* @type {import('esbuild').Plugin}
*/
const esbuildProblemMatcherPlugin = {
name: 'esbuild-problem-matcher',

setup(build) {
build.onStart(() => {
// [watch] 这个不能删除,会影响 $esbuild-watch 的工作
console.log('[watch] build started');
});
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`);
console.error(` ${location.file}:${location.line}:${location.column}:`);
});
// [watch] 这个不能删除,会影响 $esbuild-watch 的工作
console.log('[watch] build finished');
});
},
};

async function main() {
const ctx = await esbuild.context({
entryPoints: ['src/service/rpcServer/rpcProcess.ts'],
bundle: true,
platform: 'node',
minify: production,
sourcemap: !production,
sourcesContent: false,
outfile: 'dist/ng-helper-rpc-server.js',
logLevel: 'silent',
plugins: [
/* add to the end of plugins array */
esbuildProblemMatcherPlugin,
],
});
if (watch) {
await ctx.watch();
} else {
await ctx.rebuild();
await ctx.dispose();
}
}

main().catch((e) => {
console.error(e);
process.exit(1);
});
94 changes: 35 additions & 59 deletions packages/ng-helper-vscode/src/activate.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,64 @@
import type { InjectionCheckMode } from '@ng-helper/shared/lib/plugin';
import { Uri, commands, workspace } from 'vscode';
import { commands, RelativePattern, window, workspace, type ExtensionContext } from 'vscode';

import { NgHelperConfig, readUserConfig, getUserConfigFileUri } from './config';
import { EXT_CONF_PATH, EXT_IS_ACTIVATED, defaultPort } from './constants';
import { configTsPluginConfiguration } from './service/config';
import { getWorkspacePath, isFileExistsOnWorkspace, normalizeFileExt } from './utils';
import { logger } from './logger';
import { configTsPluginConfiguration } from './service/configTsPlugin';
import { getWorkspacePath, isFileExistsOnWorkspace } from './utils';

export async function activateExt(vscodeContext: ExtensionContext): Promise<NgHelperConfig | undefined> {
/**
* 监听用户配置文件的变化。
* 如果变化了,需要重新初始化插件才能生效。
* 里面简单弹出一个对话框,让用户 reloadWindow。
*/
watchUserConfig(vscodeContext);

export async function activateExt(): Promise<NgHelperConfigWithPort | undefined> {
const canActivated = await canActivate();
if (!canActivated) {
return;
}

const config = await readConfig();
const userConfig = await readUserConfig();

const port = await configTsPluginConfiguration(defaultPort, config);
logger.logInfo('====> ng-helper user config: ', userConfig);

const port = await configTsPluginConfiguration(defaultPort, userConfig);
if (!port) {
return;
}

await commands.executeCommand('setContext', EXT_IS_ACTIVATED, true);

return Object.assign(config, { port });
return new NgHelperConfig(userConfig, port);
}

async function canActivate(): Promise<boolean> {
const confUri = getConfigUri();
const confUri = getUserConfigFileUri();
if (!confUri) {
return false;
}
return await isFileExistsOnWorkspace(confUri);
}

export async function readConfig(): Promise<NgHelperConfig> {
const uri = getConfigUri()!;
const uint8Array = await workspace.fs.readFile(uri);
// uint8Array to string
const jsonText = new TextDecoder().decode(uint8Array);
function watchUserConfig(vscodeContext: ExtensionContext) {
const watcher = workspace.createFileSystemWatcher(new RelativePattern(getWorkspacePath()!, EXT_CONF_PATH));

let config = getDefaultConfig();
try {
const userConfig = JSON.parse(jsonText || '{}') as NgHelperConfig;
config = Object.assign(config, userConfig);
} catch (error) {
console.error('ng-helper.json is not a valid JSON file: ', jsonText);
}
return normalizeConfig(config);
}

function getDefaultConfig(): NgHelperConfig {
return {
componentStyleFileExt: 'css',
componentScriptFileExt: 'js',
injectionCheckMode: 'count_match',
};
}
vscodeContext.subscriptions.push(watcher);

function normalizeConfig(config: NgHelperConfig): NgHelperConfig {
return {
componentStyleFileExt: normalizeFileExt(config.componentStyleFileExt),
componentScriptFileExt: normalizeFileExt(config.componentScriptFileExt),
injectionCheckMode: config.injectionCheckMode,
};
}
watcher.onDidCreate(() => handleFileChange('create'));
watcher.onDidDelete(() => handleFileChange('delete'));
watcher.onDidChange(() => handleFileChange('change'));

function getConfigUri(): Uri | undefined {
const rootWorkspaceUri = getWorkspacePath();
if (!rootWorkspaceUri) {
return;
async function handleFileChange(type: 'delete' | 'create' | 'change') {
const message = {
delete: 'The ng-helper configuration file has been deleted. Please reload the window to disable the plugin.',
create: 'The ng-helper configuration file has been created. Please reload the window to enable the plugin.',
change: 'The ng-helper configuration file has been changed. Please reload the window to apply the changes.',
}[type];
const selection = await window.showInformationMessage(message, 'OK', 'Cancel');
if (selection === 'OK') {
await commands.executeCommand('workbench.action.reloadWindow');
}
}
return Uri.joinPath(rootWorkspaceUri, EXT_CONF_PATH);
}

export interface NgHelperConfig {
/**
* like 'less', 'scss', 'css' etc, default is 'css';
*/
componentStyleFileExt: string;
/**
* 'js' or 'ts', default is 'js';
*/
componentScriptFileExt: string;
injectionCheckMode: InjectionCheckMode;
}

export interface NgHelperConfigWithPort extends NgHelperConfig {
port: number;
}
10 changes: 6 additions & 4 deletions packages/ng-helper-vscode/src/asyncUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { CancellationToken, CancellationTokenSource } from 'vscode';

import { logger } from './logger';

const countMap = new Map<string, number>();

type TimeoutWithMeasureOptions = {
Expand Down Expand Up @@ -32,22 +34,22 @@ export async function withTimeoutAndMeasure<T>(

if (!silent) {
console.groupCollapsed(`[timeoutWithMeasure] ${label}()#${cnt}`);
console.log(`${label}()#${cnt} start...`);
logger.logInfo(`${label}()#${cnt} start...`);
}
try {
return await Promise.race([cb(), createTimeoutPromise(timeout, cancelTokenSource)]);
} catch (error) {
hasError = true;
console.error(`${label}()#${cnt} error:`, error);
logger.logError(`${label}()#${cnt} error:`, error);
} finally {
if (!hasError) {
const cost = Date.now() - start;
if (cost >= slowThreshold) {
console.warn(`${label}()#${cnt} cost ${cost}ms`);
logger.logWarning(`${label}()#${cnt} cost ${cost}ms`);
}
}
if (!silent) {
console.log(`${label}()#${cnt} end.`);
logger.logInfo(`${label}()#${cnt} end.`);
console.groupEnd();
}
}
Expand Down
Loading