@stacksjs/defaults 0.75.62 → 0.75.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/ide/vscode/build.ts +0 -52
- package/ide/vscode/scripts/package.ts +0 -71
- package/ide/vscode/scripts/stx-contributes.ts +0 -78
- package/ide/vscode/scripts/ts-plugin.ts +0 -72
- package/ide/vscode/src/env-file.ts +0 -103
- package/ide/vscode/src/env.ts +0 -241
- package/ide/vscode/src/extension.ts +0 -163
- package/ide/vscode/src/pickier-protocol.ts +0 -51
- package/ide/vscode/src/pickier-worker.ts +0 -92
- package/ide/vscode/src/pickier.ts +0 -433
- package/ide/vscode/src/project.ts +0 -450
- package/ide/vscode/src/stacks.ts +0 -482
- package/ide/vscode/tests/build.test.ts +0 -82
- package/ide/vscode/tests/env.test.ts +0 -166
- package/ide/vscode/tests/extension.test.ts +0 -428
- package/ide/vscode/tests/fake-vscode.ts +0 -228
- package/ide/vscode/tests/manifest.test.ts +0 -141
- package/ide/vscode/tests/package.test.ts +0 -47
- package/ide/vscode/tests/pickier.test.ts +0 -202
- package/ide/vscode/tests/project.test.ts +0 -268
- package/ide/vscode/tests/ts-plugin-harness.ts +0 -39
- package/ide/vscode/tsconfig.json +0 -19
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The extension entry VS Code loads (`main` in package.json, bundled to
|
|
3
|
-
* dist/extension.js). It wires the real machine into the controllers and
|
|
4
|
-
* starts each part; the behavior lives in the modules it imports:
|
|
5
|
-
*
|
|
6
|
-
* - ./stacks.ts preview, dev server and buddy commands
|
|
7
|
-
* - ./pickier.ts lint diagnostics, formatting and fix-all from the project's pickier
|
|
8
|
-
* - ./env.ts decrypted env values on hover, missing keys, `buddy env:*` commands
|
|
9
|
-
* - stx the stx extension's language support, built in from `@stacksjs/stx-vscode`,
|
|
10
|
-
* and its TypeScript server plugin, which type-checks `.stx` files
|
|
11
|
-
*
|
|
12
|
-
* Nothing here depends on another extension being installed.
|
|
13
|
-
*/
|
|
14
|
-
import type { PickierProcess } from './pickier'
|
|
15
|
-
import type { Host } from './stacks'
|
|
16
|
-
import { execFile, spawn } from 'node:child_process'
|
|
17
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
18
|
-
import { connect } from 'node:net'
|
|
19
|
-
import { join } from 'node:path'
|
|
20
|
-
import process from 'node:process'
|
|
21
|
-
import { createInterface } from 'node:readline'
|
|
22
|
-
import { activateStxLanguage, configureTypeScriptPlugin, deactivateStxLanguage, STX_EXTENSION_ID } from '@stacksjs/stx-vscode'
|
|
23
|
-
import * as vscode from 'vscode'
|
|
24
|
-
import { createEnvSupport, projectDecryptor } from './env'
|
|
25
|
-
import { createPickierSupport } from './pickier'
|
|
26
|
-
import { createStacksExtension } from './stacks'
|
|
27
|
-
|
|
28
|
-
const PROBE_TIMEOUT_MS = 500
|
|
29
|
-
|
|
30
|
-
function read(path: string): string | undefined {
|
|
31
|
-
try {
|
|
32
|
-
return readFileSync(path, 'utf8')
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return undefined
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function exec(file: string, args: string[], cwd: string): Promise<string> {
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
execFile(file, args, {
|
|
42
|
-
cwd,
|
|
43
|
-
timeout: 60_000,
|
|
44
|
-
maxBuffer: 32 * 1024 * 1024,
|
|
45
|
-
env: { ...process.env, NO_COLOR: '1', FORCE_COLOR: '0' },
|
|
46
|
-
}, (error, stdout) => error ? reject(error) : resolve(String(stdout)))
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const envHost = { exists: existsSync, read, exec, environment: process.env }
|
|
51
|
-
|
|
52
|
-
const host: Host = {
|
|
53
|
-
exists: path => existsSync(path),
|
|
54
|
-
read,
|
|
55
|
-
exec,
|
|
56
|
-
|
|
57
|
-
probe(hostname, port) {
|
|
58
|
-
return new Promise((resolve) => {
|
|
59
|
-
const socket = connect({ host: hostname, port })
|
|
60
|
-
const finish = (open: boolean) => {
|
|
61
|
-
socket.destroy()
|
|
62
|
-
resolve(open)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
socket.setTimeout(PROBE_TIMEOUT_MS)
|
|
66
|
-
socket.once('connect', () => finish(true))
|
|
67
|
-
socket.once('timeout', () => finish(false))
|
|
68
|
-
socket.once('error', () => finish(false))
|
|
69
|
-
})
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
sleep: ms => new Promise(resolve => setTimeout(resolve, ms)),
|
|
73
|
-
|
|
74
|
-
decryptEnv: root => projectDecryptor(envHost, root),
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* `bun --config=<empty> dist/pickier-worker.js` in the project root. The
|
|
79
|
-
* explicit config keeps the project's bunfig.toml out: its `preload` boots the
|
|
80
|
-
* framework (env decryption, auto-imports), which pickier does not need.
|
|
81
|
-
*/
|
|
82
|
-
function spawnBun(bun: string, script: string, cwd: string): PickierProcess {
|
|
83
|
-
const child = spawn(bun, [`--config=${join(script, '..', 'pickier-worker.bunfig.toml')}`, script], {
|
|
84
|
-
cwd,
|
|
85
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
86
|
-
env: { ...process.env, NO_COLOR: '1', FORCE_COLOR: '0' },
|
|
87
|
-
})
|
|
88
|
-
const lines = createInterface({ input: child.stdout })
|
|
89
|
-
let exited = false
|
|
90
|
-
const exitListeners: Array<() => void> = []
|
|
91
|
-
const exit = () => {
|
|
92
|
-
if (exited)
|
|
93
|
-
return
|
|
94
|
-
exited = true
|
|
95
|
-
for (const listener of exitListeners)
|
|
96
|
-
listener()
|
|
97
|
-
}
|
|
98
|
-
child.once('exit', exit)
|
|
99
|
-
child.once('error', exit)
|
|
100
|
-
child.stderr.resume()
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
write: line => child.stdin.write(line),
|
|
104
|
-
onLine: listener => lines.on('line', listener),
|
|
105
|
-
onExit: listener => exitListeners.push(listener),
|
|
106
|
-
kill: () => child.kill(),
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const stacks = createStacksExtension(vscode, host)
|
|
111
|
-
const env = createEnvSupport(vscode, envHost)
|
|
112
|
-
let pickier: ReturnType<typeof createPickierSupport> | undefined
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* What `activate` resolves to. The stx extension (Stacks.vscode-stx) reads
|
|
116
|
-
* `stx.active` and stands down when it is true, so the two never register
|
|
117
|
-
* stx support twice in one window.
|
|
118
|
-
*/
|
|
119
|
-
export interface StacksExtensionApi {
|
|
120
|
-
stx: { active: boolean }
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function startStx(context: vscode.ExtensionContext): Promise<boolean> {
|
|
124
|
-
try {
|
|
125
|
-
await activateStxLanguage(context, {
|
|
126
|
-
assetsPath: join(context.extensionPath, 'dist', 'stx'),
|
|
127
|
-
// The stx extension contributes the snippets in its manifest; offering
|
|
128
|
-
// them here as well would show each one twice.
|
|
129
|
-
snippets: !vscode.extensions.getExtension(STX_EXTENSION_ID),
|
|
130
|
-
})
|
|
131
|
-
return true
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
console.error('Stacks: stx support failed to start', error)
|
|
135
|
-
return false
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export async function activate(context: vscode.ExtensionContext): Promise<StacksExtensionApi> {
|
|
140
|
-
// The manifest contributes the stx TypeScript server plugin. VS Code's
|
|
141
|
-
// TypeScript extension does not activate on `stx` by itself, so this starts
|
|
142
|
-
// it, and forwards `stxTypescriptPlugin.enabled` to the plugin. The stx
|
|
143
|
-
// extension, when installed, does the same; the plugin decorates a project
|
|
144
|
-
// once however many extensions contribute it.
|
|
145
|
-
configureTypeScriptPlugin(vscode, context.subscriptions).catch((error) => {
|
|
146
|
-
console.error('Stacks: could not configure the stx TypeScript plugin', error)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
stacks.activate(context)
|
|
150
|
-
env.activate(context)
|
|
151
|
-
pickier = createPickierSupport(vscode, { exists: existsSync, spawnBun }, {
|
|
152
|
-
workerPath: join(context.extensionPath, 'dist', 'pickier-worker.js'),
|
|
153
|
-
})
|
|
154
|
-
pickier.activate(context)
|
|
155
|
-
|
|
156
|
-
return { stx: { active: await startStx(context) } }
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function deactivate(): void {
|
|
160
|
-
stacks.deactivate()
|
|
161
|
-
pickier?.deactivate()
|
|
162
|
-
deactivateStxLanguage()
|
|
163
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The line protocol between the extension and dist/pickier-worker.js.
|
|
3
|
-
*
|
|
4
|
-
* Pickier runs on Bun (its config is a TypeScript file, and it uses Bun's glob
|
|
5
|
-
* and YAML APIs), and VS Code runs extensions on Node, so the extension keeps
|
|
6
|
-
* one Bun process per project and talks to it over stdin/stdout: one JSON
|
|
7
|
-
* object per line each way, answers matched to requests by `id`.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export type PickierOperation = 'lint' | 'fix' | 'format'
|
|
11
|
-
|
|
12
|
-
export interface PickierRequest {
|
|
13
|
-
id: number
|
|
14
|
-
op: PickierOperation
|
|
15
|
-
/** Absolute path, for config ignores and the per-extension rules. */
|
|
16
|
-
file: string
|
|
17
|
-
text: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** One issue, as pickier's `lintText` reports it: 1-based line and column. */
|
|
21
|
-
export interface PickierIssue {
|
|
22
|
-
line: number
|
|
23
|
-
column: number
|
|
24
|
-
ruleId: string
|
|
25
|
-
message: string
|
|
26
|
-
severity: 'error' | 'warning'
|
|
27
|
-
help?: string
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type PickierResult =
|
|
31
|
-
| { op: 'lint', issues: PickierIssue[] }
|
|
32
|
-
/** `text` is null when the project's pickier predates `fixText`, or the file is ignored. */
|
|
33
|
-
| { op: 'fix' | 'format', text: string | null }
|
|
34
|
-
|
|
35
|
-
export type PickierResponse =
|
|
36
|
-
| { id: number, ok: true, result: PickierResult }
|
|
37
|
-
| { id: number, ok: false, error: string, missing?: boolean }
|
|
38
|
-
|
|
39
|
-
/** The first line the worker writes, once it has loaded the project's pickier and config. */
|
|
40
|
-
export interface PickierReady {
|
|
41
|
-
ready: true
|
|
42
|
-
version: string
|
|
43
|
-
fixText: boolean
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** What the worker writes instead when the project has no pickier it can load. */
|
|
47
|
-
export interface PickierUnavailable {
|
|
48
|
-
ready: false
|
|
49
|
-
error: string
|
|
50
|
-
missing: boolean
|
|
51
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Bun side of the extension's pickier support (bundled to
|
|
3
|
-
* dist/pickier-worker.js, started with the project root as its working
|
|
4
|
-
* directory). See ./pickier-protocol.ts.
|
|
5
|
-
*
|
|
6
|
-
* It loads the PROJECT's pickier, not a copy bundled into the extension, and
|
|
7
|
-
* lets pickier find the project's config itself, the way `./buddy lint` does
|
|
8
|
-
* (core/actions/src/lint/lint.ts). Editor and command therefore agree on the
|
|
9
|
-
* rules, the version and the ignores.
|
|
10
|
-
*/
|
|
11
|
-
import type { PickierReady, PickierRequest, PickierResponse, PickierResult, PickierUnavailable } from './pickier-protocol'
|
|
12
|
-
import { relative } from 'node:path'
|
|
13
|
-
import process from 'node:process'
|
|
14
|
-
|
|
15
|
-
interface PickierModule {
|
|
16
|
-
lintText: (text: string, config: unknown, filePath?: string) => Promise<Array<{ line: number, column: number, ruleId: string, message: string, severity: 'error' | 'warning', help?: string }>>
|
|
17
|
-
formatCode: (text: string, config: unknown, filePath: string) => string
|
|
18
|
-
fixText?: (text: string, config: unknown, filePath?: string) => string
|
|
19
|
-
loadConfigFromPath: (path: string | undefined) => Promise<{ ignores?: string[] }>
|
|
20
|
-
shouldIgnorePath?: (absPath: string, ignoreGlobs: string[]) => boolean
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function send(message: PickierResponse | PickierReady | PickierUnavailable): void {
|
|
24
|
-
process.stdout.write(`${JSON.stringify(message)}\n`)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const root = process.cwd()
|
|
28
|
-
let pickier: PickierModule
|
|
29
|
-
let config: { ignores?: string[] }
|
|
30
|
-
let version = 'unknown'
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const entry = Bun.resolveSync('pickier', root)
|
|
34
|
-
pickier = await import(entry) as PickierModule
|
|
35
|
-
try {
|
|
36
|
-
version = (await import(Bun.resolveSync('pickier/package.json', root))).version ?? version
|
|
37
|
-
}
|
|
38
|
-
catch {}
|
|
39
|
-
config = await pickier.loadConfigFromPath(undefined)
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
// Not a pickier project (or not installed yet): say so and stop, so the
|
|
43
|
-
// extension stops asking instead of retrying on every keystroke.
|
|
44
|
-
send({ ready: false, error: String(error), missing: true })
|
|
45
|
-
process.exit(0)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
send({ ready: true, version, fixText: typeof pickier.fixText === 'function' })
|
|
49
|
-
|
|
50
|
-
function ignored(file: string): boolean {
|
|
51
|
-
const rel = relative(root, file)
|
|
52
|
-
if (rel.startsWith('..'))
|
|
53
|
-
return true
|
|
54
|
-
return pickier.shouldIgnorePath?.(file, config.ignores ?? []) ?? false
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function handle(request: PickierRequest): Promise<PickierResult> {
|
|
58
|
-
if (request.op === 'lint') {
|
|
59
|
-
if (ignored(request.file))
|
|
60
|
-
return { op: 'lint', issues: [] }
|
|
61
|
-
const issues = await pickier.lintText(request.text, config, request.file)
|
|
62
|
-
return { op: 'lint', issues: issues.map(({ line, column, ruleId, message, severity, help }) => ({ line, column, ruleId, message, severity, help })) }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (ignored(request.file))
|
|
66
|
-
return { op: request.op, text: null }
|
|
67
|
-
|
|
68
|
-
if (request.op === 'fix')
|
|
69
|
-
return { op: 'fix', text: pickier.fixText ? pickier.fixText(request.text, config, request.file) : null }
|
|
70
|
-
|
|
71
|
-
return { op: 'format', text: pickier.formatCode(request.text, config, request.file) }
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for await (const line of console) {
|
|
75
|
-
if (!line.trim())
|
|
76
|
-
continue
|
|
77
|
-
|
|
78
|
-
let request: PickierRequest
|
|
79
|
-
try {
|
|
80
|
-
request = JSON.parse(line)
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
continue
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
send({ id: request.id, ok: true, result: await handle(request) })
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
send({ id: request.id, ok: false, error: error instanceof Error ? error.message : String(error) })
|
|
91
|
-
}
|
|
92
|
-
}
|