Skip to content

Commit 5ca0d8c

Browse files
feat: dedicated ide config command
1 parent d166a16 commit 5ca0d8c

7 files changed

Lines changed: 220 additions & 75 deletions

File tree

cli/README.md

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ See [docs/usage.md](../docs/usage.md) for full usage and resolution order (flags
256256

257257
- [`powersync autocomplete [SHELL]`](#powersync-autocomplete-shell)
258258
- [`powersync commands`](#powersync-commands)
259+
- [`powersync configure ide`](#powersync-configure-ide)
259260
- [`powersync deploy`](#powersync-deploy)
260261
- [`powersync deploy service-config`](#powersync-deploy-service-config)
261262
- [`powersync deploy sync-config`](#powersync-deploy-sync-config)
@@ -353,6 +354,26 @@ DESCRIPTION
353354
354355
_See code: [@oclif/plugin-commands](https://github.com/oclif/plugin-commands/blob/v4.1.40/src/commands/commands.ts)_
355356
357+
## `powersync configure ide`
358+
359+
Configure your IDE for the best PowerSync CLI developer experience.
360+
361+
```
362+
USAGE
363+
$ powersync configure ide
364+
365+
DESCRIPTION
366+
Configure your IDE for the best PowerSync CLI developer experience.
367+
368+
Configure or guide your IDE setup for the best PowerSync CLI developer experience. Enables YAML schema validation and
369+
autocompletion, sets up !env custom tag support, and patches existing config files with language server directives.
370+
371+
EXAMPLES
372+
$ powersync configure ide
373+
```
374+
375+
_See code: [src/commands/configure/ide.ts](https://github.com/powersync-ja/powersync-js/blob/v0.0.0/src/commands/configure/ide.ts)_
376+
356377
## `powersync deploy`
357378
358379
[Cloud only] Deploy local config to the linked Cloud instance (connections + auth + sync config).
@@ -901,10 +922,7 @@ Scaffold a PowerSync Cloud config directory from a template.
901922
902923
```
903924
USAGE
904-
$ powersync init cloud [--directory <value>] [--vscode]
905-
906-
FLAGS
907-
--vscode Configure the workspace with .vscode settings for YAML custom tags (!env).
925+
$ powersync init cloud [--directory <value>]
908926

909927
PROJECT FLAGS
910928
--directory=<value> [default: powersync] Directory containing PowerSync config. Defaults to "powersync". This is
@@ -919,7 +937,7 @@ DESCRIPTION
919937
EXAMPLES
920938
$ powersync init cloud
921939

922-
$ powersync init cloud --directory=powersync --vscode
940+
$ powersync init cloud --directory=powersync
923941
```
924942
925943
_See code: [src/commands/init/cloud.ts](https://github.com/powersync-ja/powersync-js/blob/v0.0.0/src/commands/init/cloud.ts)_
@@ -930,10 +948,7 @@ Scaffold a PowerSync self-hosted config directory from a template.
930948
931949
```
932950
USAGE
933-
$ powersync init self-hosted [--directory <value>] [--vscode]
934-
935-
FLAGS
936-
--vscode Configure the workspace with .vscode settings for YAML custom tags (!env).
951+
$ powersync init self-hosted [--directory <value>]
937952

938953
PROJECT FLAGS
939954
--directory=<value> [default: powersync] Directory containing PowerSync config. Defaults to "powersync". This is
@@ -949,7 +964,7 @@ DESCRIPTION
949964
EXAMPLES
950965
$ powersync init self-hosted
951966

952-
$ powersync init self-hosted --directory=powersync --vscode
967+
$ powersync init self-hosted --directory=powersync
953968
```
954969
955970
_See code: [src/commands/init/self-hosted.ts](https://github.com/powersync-ja/powersync-js/blob/v0.0.0/src/commands/init/self-hosted.ts)_
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { ux } from '@oclif/core';
2+
import {
3+
CLI_FILENAME,
4+
SERVICE_FILENAME,
5+
SYNC_FILENAME,
6+
YAML_CLI_SCHEMA,
7+
YAML_SERVICE_SCHEMA,
8+
YAML_SYNC_RULES_SCHEMA
9+
} from '@powersync/cli-core';
10+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
11+
import { join } from 'node:path';
12+
13+
const VSCODE_YAML_TAGS = ['!env scalar'];
14+
15+
/** Maps each known config filename to its yaml-language-server schema comment. */
16+
const YAML_SCHEMA_COMMENTS: Record<string, string> = {
17+
[CLI_FILENAME]: YAML_CLI_SCHEMA,
18+
[SERVICE_FILENAME]: YAML_SERVICE_SCHEMA,
19+
[SYNC_FILENAME]: YAML_SYNC_RULES_SCHEMA
20+
};
21+
22+
/**
23+
* A pluggable function signature for configuring a specific IDE.
24+
* Receives the workspace root (for IDE settings), the list of discovered project
25+
* directories to scan, and a log callback for producing output.
26+
*/
27+
export type IdeConfigurator = (workspaceRoot: string, projectDirs: string[], log: (message: string) => void) => void;
28+
29+
/**
30+
* Configures the VSCode workspace for PowerSync YAML editing and prints guidance:
31+
* - Writes/merges .vscode/settings.json with yaml.customTags so the !env tag is recognised.
32+
* - Scans each projectDir for known config files and prepends a yaml-language-server schema
33+
* comment to any file that does not already have one.
34+
* - Prints a summary of changes, extension recommendation, and schema comment reference.
35+
*/
36+
export function configureVscodeIde(workspaceRoot: string, projectDirs: string[], log: (message: string) => void): void {
37+
const vscodeDir = join(workspaceRoot, '.vscode');
38+
const settingsPath = join(vscodeDir, 'settings.json');
39+
40+
let settings: Record<string, unknown> = {};
41+
if (existsSync(settingsPath)) {
42+
try {
43+
const raw = readFileSync(settingsPath, 'utf8');
44+
settings = JSON.parse(raw) as Record<string, unknown>;
45+
} catch {
46+
// If invalid JSON, overwrite with our settings.
47+
}
48+
}
49+
50+
const currentTags = (settings['yaml.customTags'] ?? []) as string[];
51+
settings['yaml.customTags'] = [...new Set([...currentTags, ...VSCODE_YAML_TAGS])];
52+
mkdirSync(vscodeDir, { recursive: true });
53+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
54+
55+
const filesUpdated: string[] = [];
56+
57+
for (const projectDir of projectDirs) {
58+
for (const [filename, schemaComment] of Object.entries(YAML_SCHEMA_COMMENTS)) {
59+
const filePath = join(projectDir, filename);
60+
if (!existsSync(filePath)) continue;
61+
62+
const content = readFileSync(filePath, 'utf8');
63+
if (!content.includes('yaml-language-server:')) {
64+
writeFileSync(filePath, `${schemaComment}\n\n${content}`);
65+
filesUpdated.push(filePath);
66+
}
67+
}
68+
}
69+
70+
const lines: string[] = [
71+
ux.colorize('green', 'VSCode configured for PowerSync YAML editing!'),
72+
'',
73+
`✔ Updated .vscode/settings.json with yaml.customTags: ${JSON.stringify(VSCODE_YAML_TAGS)}`
74+
];
75+
76+
if (filesUpdated.length > 0) {
77+
lines.push('', 'Added yaml-language-server schema comments to:');
78+
for (const f of filesUpdated) {
79+
lines.push(` ✔ ${f}`);
80+
}
81+
}
82+
83+
lines.push(
84+
'',
85+
ux.colorize('cyan', 'Recommended: Install the YAML extension'),
86+
'Install the Red Hat YAML extension for VSCode to get schema validation and autocompletion:',
87+
ux.colorize('blue', ' https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml'),
88+
'',
89+
ux.colorize('cyan', 'Language server schema comments'),
90+
'The following comments at the top of each YAML config file activate schema support.',
91+
'They are added automatically when you run powersync init, but you can also add them manually:',
92+
'',
93+
ux.colorize('dim', '# service.yaml'),
94+
ux.colorize('gray', YAML_SERVICE_SCHEMA),
95+
'',
96+
ux.colorize('dim', '# sync-config.yaml'),
97+
ux.colorize('gray', YAML_SYNC_RULES_SCHEMA),
98+
'',
99+
ux.colorize('dim', '# cli.yaml'),
100+
ux.colorize('gray', YAML_CLI_SCHEMA)
101+
);
102+
103+
log(lines.join('\n'));
104+
}

cli/src/api/write-vscode-settings-for-yaml-env.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

cli/src/commands/configure/ide.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { select } from '@inquirer/prompts';
2+
import { ux } from '@oclif/core';
3+
import { CLI_FILENAME, parseYamlFile, PowerSyncCommand } from '@powersync/cli-core';
4+
import { CLIConfig } from '@powersync/cli-schemas';
5+
import { readdirSync, statSync } from 'node:fs';
6+
import { join } from 'node:path';
7+
8+
import { configureVscodeIde, type IdeConfigurator } from '../../api/ide/configure-vscode-ide.js';
9+
10+
const IDE_CONFIGURATORS: Record<string, IdeConfigurator> = {
11+
vscode: configureVscodeIde
12+
};
13+
14+
/**
15+
* Scans the current working directory for subdirectories that contain a valid
16+
* PowerSync cli.yaml. Returns their absolute paths.
17+
*/
18+
function findLinkedProjectDirs(cwd: string): string[] {
19+
const projectDirs: string[] = [];
20+
21+
for (const entry of readdirSync(cwd)) {
22+
const entryPath = join(cwd, entry);
23+
try {
24+
if (!statSync(entryPath).isDirectory()) continue;
25+
} catch {
26+
continue;
27+
}
28+
29+
try {
30+
const doc = parseYamlFile(join(entryPath, CLI_FILENAME));
31+
CLIConfig.decode(doc.contents?.toJSON());
32+
projectDirs.push(entryPath);
33+
} catch {
34+
// Not a valid PowerSync project — skip.
35+
}
36+
}
37+
38+
return projectDirs;
39+
}
40+
41+
export default class ConfigureIde extends PowerSyncCommand {
42+
static description =
43+
'Configure or guide your IDE setup for the best PowerSync CLI developer experience. Enables YAML schema validation and autocompletion, sets up !env custom tag support, and patches existing config files with language server directives.';
44+
static examples = ['<%= config.bin %> <%= command.id %>'];
45+
static summary = 'Configure your IDE for the best PowerSync CLI developer experience.';
46+
47+
async run(): Promise<void> {
48+
await this.parse(ConfigureIde);
49+
50+
this.log(
51+
`Tip: use ${ux.colorize('blue', 'powersync edit config')} for a complete in-browser editing experience.\n`
52+
);
53+
54+
const ide = await select({
55+
choices: [
56+
{ name: 'VSCode', value: 'vscode' },
57+
{ name: 'Exit', value: 'exit' }
58+
],
59+
message: 'Select your IDE to configure (only VSCode is supported for now):'
60+
});
61+
62+
if (ide === 'exit') return;
63+
64+
const projectDirs = findLinkedProjectDirs(process.cwd());
65+
const configurator = IDE_CONFIGURATORS[ide];
66+
configurator(process.cwd(), projectDirs, (msg) => this.log(msg));
67+
}
68+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Command } from '@oclif/core';
2+
3+
export default class Configure extends Command {
4+
static description = 'Configure your workspace or IDE for PowerSync development.';
5+
static examples = ['<%= config.bin %> <%= command.id %>'];
6+
static hidden = true;
7+
static summary = 'Configure your workspace or IDE for PowerSync development.';
8+
9+
async run(): Promise<void> {
10+
await this.parse(Configure);
11+
this.log('Use a subcommand: configure ide');
12+
}
13+
}

cli/src/commands/init/cloud.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Flags, ux } from '@oclif/core';
1+
import { ux } from '@oclif/core';
22
import {
33
CLI_FILENAME,
44
InstanceCommand,
@@ -12,8 +12,6 @@ import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node
1212
import { dirname, join } from 'node:path';
1313
import { fileURLToPath } from 'node:url';
1414

15-
import { writeVscodeSettingsForYamlEnv } from '../../api/write-vscode-settings-for-yaml-env.js';
16-
1715
const __dirname = dirname(fileURLToPath(import.meta.url));
1816
const TEMPLATES_DIR = join(__dirname, '..', '..', '..', 'templates');
1917

@@ -22,20 +20,16 @@ export default class InitCloud extends InstanceCommand {
2220
'Copy a Cloud template into a config directory (default powersync/). Edit service.yaml then run link cloud and deploy.';
2321
static examples = [
2422
'<%= config.bin %> <%= command.id %>',
25-
'<%= config.bin %> <%= command.id %> --directory=powersync --vscode'
23+
'<%= config.bin %> <%= command.id %> --directory=powersync'
2624
];
2725
static flags = {
28-
...InstanceCommand.flags,
29-
vscode: Flags.boolean({
30-
default: false,
31-
description: 'Configure the workspace with .vscode settings for YAML custom tags (!env).'
32-
})
26+
...InstanceCommand.flags
3327
};
3428
static summary = 'Scaffold a PowerSync Cloud config directory from a template.';
3529

3630
async run(): Promise<void> {
3731
const { flags } = await this.parse(InitCloud);
38-
const { directory, vscode } = flags;
32+
const { directory } = flags;
3933
const targetDir = this.resolveProjectDir(flags);
4034

4135
if (existsSync(targetDir)) {
@@ -62,10 +56,6 @@ export default class InitCloud extends InstanceCommand {
6256
writeFileSync(syncPath, `${YAML_SYNC_RULES_SCHEMA}\n\n${readFileSync(syncPath, 'utf8')}`);
6357
writeFileSync(cliPath, `${YAML_CLI_SCHEMA}\n\n${readFileSync(cliPath, 'utf8')}`);
6458

65-
if (vscode) {
66-
writeVscodeSettingsForYamlEnv(process.cwd());
67-
}
68-
6959
const instructions = [
7060
'Create a new instance with ',
7161
ux.colorize('blue', '\tpowersync link cloud --create --org-id=<org-id> --project-id=<project-id>'),
@@ -86,13 +76,10 @@ export default class InitCloud extends InstanceCommand {
8676
'Configuration files are located in:',
8777
`\t${targetDir}`,
8878
`Check the ${SERVICE_FILENAME} and ${SYNC_FILENAME} file(s) and configure them by uncommenting the options you would like to use.`,
79+
`Tip: Run ${ux.colorize('blue', 'powersync configure ide')} to configure your IDE for YAML schema support.`,
8980
'',
9081
instructions
9182
];
92-
if (vscode) {
93-
lines.splice(6, 0, 'Added .vscode/settings.json for YAML !env tag support.');
94-
lines.splice(7, 0, '');
95-
}
9683

9784
this.log(lines.join('\n'));
9885
}

0 commit comments

Comments
 (0)