@stacksjs/defaults 0.75.46 → 0.75.48
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/ai/skills/stacks-chat/SKILL.md +5 -0
- package/ide/vscode/README.md +4 -2
- package/ide/vscode/build.ts +11 -2
- package/ide/vscode/package.json +14 -4
- package/ide/vscode/scripts/package.ts +71 -0
- package/ide/vscode/scripts/stx-contributes.ts +4 -1
- package/ide/vscode/scripts/ts-plugin.ts +72 -0
- package/ide/vscode/src/extension.ts +12 -2
- package/ide/vscode/tests/build.test.ts +24 -0
- package/ide/vscode/tests/manifest.test.ts +40 -0
- package/ide/vscode/tests/package.test.ts +47 -0
- package/ide/vscode/tests/ts-plugin-harness.ts +39 -0
- package/package.json +2 -2
|
@@ -385,6 +385,11 @@ controller the driver only opens the conversation in Messages for the person to
|
|
|
385
385
|
from Recently Deleted, and `confirmDeletes: true` accepts Messages' alert too.
|
|
386
386
|
Attic (`~/Code/Apps/attic`, `app/Desktop/messages-control.swift`) is the reference.
|
|
387
387
|
|
|
388
|
+
A conversation deleted in Messages sits in Recently Deleted for 30 days:
|
|
389
|
+
`conversation.visible` is false and `conversation.deleted` counts what is still
|
|
390
|
+
recoverable. `messages(id, { includeDeleted: true })` reads those messages, so an
|
|
391
|
+
app can keep a deleted conversation without recovering it in Messages.
|
|
392
|
+
|
|
388
393
|
Discord personal DMs are deliberately absent: only a user token reads them, and
|
|
389
394
|
automating a user account breaks Discord's terms. Reactions are separate
|
|
390
395
|
`kind: 'reaction'` messages with `targetId` and an emoji `reaction`; Slack and
|
package/ide/vscode/README.md
CHANGED
|
@@ -8,7 +8,9 @@ The Stacks commands, pickier and the env features activate in workspaces that co
|
|
|
8
8
|
|
|
9
9
|
`.stx` files get the full stx language support: highlighting, completions for directives, components and props, hovers, diagnostics, go to definition, folding, semantic highlighting, snippets, and previews of utility classes with their colors.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The TypeScript in `.stx` files, `<script>` blocks and `{{ }}` template expressions alike, is type-checked by the TypeScript server, with its hovers, completions and go to definition, against the same declarations `stx typecheck` uses: `import { state } from 'stx'` resolves to exactly what the runtime binds. Turn it off with `stxTypescriptPlugin.enabled`.
|
|
12
|
+
|
|
13
|
+
It is the same code as the standalone [stx extension](https://marketplace.visualstudio.com/items?itemName=Stacks.vscode-stx) (`Stacks.vscode-stx`), built in from `@stacksjs/stx-vscode`. You do not need that extension; if you have both, it stands down so nothing is registered twice, and the TypeScript plugin both contribute checks each file once.
|
|
12
14
|
|
|
13
15
|
## Pickier
|
|
14
16
|
|
|
@@ -97,7 +99,7 @@ bun run package # build and create a .vsix
|
|
|
97
99
|
code --install-extension vscode-stacks-<version>.vsix
|
|
98
100
|
```
|
|
99
101
|
|
|
100
|
-
The extension has no runtime dependencies: `@stacksjs/stx-vscode` and the env decryption from `core/env` are bundled into `dist/extension.js`. It is published automatically by the Stacks release workflow, and `vscode:prepublish` builds `dist/` first.
|
|
102
|
+
The extension has no runtime dependencies: `@stacksjs/stx-vscode` and the env decryption from `core/env` are bundled into `dist/extension.js`. The one exception is the stx TypeScript plugin, which tsserver loads by package name from the extension's `node_modules`: `bun run package` (and `bun run release`, which publishes that VSIX) adds it there after `vsce package --no-dependencies`, which leaves `node_modules` out. It is published automatically by the Stacks release workflow, and `vscode:prepublish` builds `dist/` first.
|
|
101
103
|
|
|
102
104
|
## Relevant links
|
|
103
105
|
|
package/ide/vscode/build.ts
CHANGED
|
@@ -5,13 +5,18 @@
|
|
|
5
5
|
* pickier-worker.js the Bun process that runs the project's pickier (src/pickier-worker.ts)
|
|
6
6
|
* pickier-worker.bunfig.toml an empty bunfig, so the worker skips the project's preloads
|
|
7
7
|
* stx/ grammar, language configuration and snippets from @stacksjs/stx-vscode
|
|
8
|
+
* stx-typescript-plugin/ the stx TypeScript server plugin from @stacksjs/stx-vscode
|
|
8
9
|
*
|
|
9
10
|
* `vsce package` runs this first (`vscode:prepublish`), and `files` in
|
|
10
|
-
* package.json ships exactly these.
|
|
11
|
+
* package.json ships exactly these. tsserver loads the plugin from
|
|
12
|
+
* node_modules, so a forwarder to it is written there as well, which is what
|
|
13
|
+
* a development host (`--extensionDevelopmentPath`) loads; scripts/package.ts
|
|
14
|
+
* adds the same forwarder to the VSIX (see scripts/ts-plugin.ts).
|
|
11
15
|
*/
|
|
12
|
-
import { cpSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
16
|
+
import { cpSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
13
17
|
import { dirname, join } from 'node:path'
|
|
14
18
|
import process from 'node:process'
|
|
19
|
+
import { copyTsPlugin, tsPluginName, writeTsPluginForwarder } from './scripts/ts-plugin'
|
|
15
20
|
|
|
16
21
|
const here = import.meta.dir
|
|
17
22
|
const dist = join(here, 'dist')
|
|
@@ -40,4 +45,8 @@ writeFileSync(join(dist, 'pickier-worker.bunfig.toml'), '# Intentionally empty:
|
|
|
40
45
|
const stxAssets = join(dirname(Bun.resolveSync('@stacksjs/stx-vscode/contributes.json', here)), 'assets')
|
|
41
46
|
cpSync(stxAssets, join(dist, 'stx'), { recursive: true })
|
|
42
47
|
|
|
48
|
+
copyTsPlugin(here)
|
|
49
|
+
const { version } = JSON.parse(readFileSync(join(here, 'package.json'), 'utf8'))
|
|
50
|
+
writeTsPluginForwarder(here, tsPluginName(here), version)
|
|
51
|
+
|
|
43
52
|
console.log('Built dist/')
|
package/ide/vscode/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"publisher": "Stacks",
|
|
3
3
|
"name": "vscode-stacks",
|
|
4
4
|
"displayName": "Stacks",
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.48",
|
|
6
6
|
"description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"dist/pickier-worker.js",
|
|
45
45
|
"dist/pickier-worker.bunfig.toml",
|
|
46
46
|
"dist/stx/**",
|
|
47
|
+
"dist/stx-typescript-plugin/**",
|
|
47
48
|
"LICENSE.md",
|
|
48
49
|
"README.md",
|
|
49
50
|
"logo.png"
|
|
@@ -72,8 +73,8 @@
|
|
|
72
73
|
"test": "bun test ./tests",
|
|
73
74
|
"typecheck": "bun x --bun tsc --noEmit -p tsconfig.json",
|
|
74
75
|
"sync:stx": "bun scripts/stx-contributes.ts --write",
|
|
75
|
-
"package": "
|
|
76
|
-
"release": "
|
|
76
|
+
"package": "bun scripts/package.ts",
|
|
77
|
+
"release": "bun scripts/package.ts --publish"
|
|
77
78
|
},
|
|
78
79
|
"galleryBanner": {
|
|
79
80
|
"color": "#d7e4f5",
|
|
@@ -410,10 +411,19 @@
|
|
|
410
411
|
"meta.component.parameters.stx": "typescript"
|
|
411
412
|
}
|
|
412
413
|
}
|
|
414
|
+
],
|
|
415
|
+
"typescriptServerPlugins": [
|
|
416
|
+
{
|
|
417
|
+
"name": "@stacksjs/stx-typescript-plugin",
|
|
418
|
+
"enableForWorkspaceTypeScriptVersions": true,
|
|
419
|
+
"languages": [
|
|
420
|
+
"stx"
|
|
421
|
+
]
|
|
422
|
+
}
|
|
413
423
|
]
|
|
414
424
|
},
|
|
415
425
|
"devDependencies": {
|
|
416
|
-
"@stacksjs/stx-vscode": "^0.2.
|
|
426
|
+
"@stacksjs/stx-vscode": "^0.2.358",
|
|
417
427
|
"@types/vscode": "~1.109.0",
|
|
418
428
|
"@vscode/vsce": "^4.0.0"
|
|
419
429
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package (and with `--publish`, publish) the extension.
|
|
3
|
+
*
|
|
4
|
+
* bun scripts/package.ts [--out <file.vsix>] [--publish]
|
|
5
|
+
*
|
|
6
|
+
* `vsce package --no-dependencies` keeps the VSIX to what `files` lists, and it
|
|
7
|
+
* leaves out everything under node_modules - including the one package the
|
|
8
|
+
* extension needs there: the stx TypeScript server plugin, which tsserver will
|
|
9
|
+
* only load by package name from `<extension>/node_modules` (see ts-plugin.ts).
|
|
10
|
+
* So its forwarder is added to the VSIX after vsce writes it, and a publish
|
|
11
|
+
* uploads that VSIX rather than letting vsce package a second time.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
|
|
14
|
+
import { tmpdir } from 'node:os'
|
|
15
|
+
import { join, resolve } from 'node:path'
|
|
16
|
+
import process from 'node:process'
|
|
17
|
+
import { TS_PLUGIN_DIST, tsPluginModuleDir, tsPluginName, writeTsPluginForwarder } from './ts-plugin'
|
|
18
|
+
|
|
19
|
+
const ROOT = join(import.meta.dir, '..')
|
|
20
|
+
|
|
21
|
+
function run(command: string[], cwd = ROOT, capture = false): string {
|
|
22
|
+
const result = Bun.spawnSync(command, { cwd, stdout: capture ? 'pipe' : 'inherit', stderr: 'inherit', env: process.env })
|
|
23
|
+
if (result.exitCode !== 0)
|
|
24
|
+
throw new Error(`${command.join(' ')} exited with ${result.exitCode}`)
|
|
25
|
+
return capture ? String(result.stdout ?? '') : ''
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Build the VSIX at `out`, with the TypeScript plugin's forwarder in it. */
|
|
29
|
+
export function packageExtension(out: string): string {
|
|
30
|
+
const manifest = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'))
|
|
31
|
+
const name = tsPluginName(ROOT)
|
|
32
|
+
|
|
33
|
+
// vsce runs `vscode:prepublish` (the build) first.
|
|
34
|
+
run(['bun', 'x', '--bun', 'vsce', 'package', '--no-dependencies', '--out', out])
|
|
35
|
+
|
|
36
|
+
// A staging tree whose layout is the VSIX's, so zip records the paths VS
|
|
37
|
+
// Code extracts: extension/node_modules/<package>/...
|
|
38
|
+
const stage = mkdtempSync(join(tmpdir(), 'stacks-vsix-'))
|
|
39
|
+
try {
|
|
40
|
+
writeTsPluginForwarder(join(stage, 'extension'), name, manifest.version)
|
|
41
|
+
run(['zip', '-q', '-r', '-X', '-D', out, `extension/${tsPluginModuleDir(name)}`], stage)
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
rmSync(stage, { recursive: true, force: true })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const listing = run(['unzip', '-Z1', out], ROOT, true).split('\n')
|
|
48
|
+
for (const file of [`extension/${tsPluginModuleDir(name)}/index.js`, `extension/${TS_PLUGIN_DIST}/index.js`, `extension/${TS_PLUGIN_DIST}/typescript-stx-plugin.js`]) {
|
|
49
|
+
if (!listing.includes(file))
|
|
50
|
+
throw new Error(`${out} is missing ${file}`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return out
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (import.meta.main) {
|
|
57
|
+
const args = process.argv.slice(2)
|
|
58
|
+
const outFlag = args.indexOf('--out')
|
|
59
|
+
const manifest = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'))
|
|
60
|
+
const out = resolve(outFlag === -1 ? join(ROOT, `${manifest.name}-${manifest.version}.vsix`) : args[outFlag + 1]!)
|
|
61
|
+
|
|
62
|
+
packageExtension(out)
|
|
63
|
+
console.log(`Packaged ${out}`)
|
|
64
|
+
|
|
65
|
+
if (args.includes('--publish')) {
|
|
66
|
+
if (!existsSync(out))
|
|
67
|
+
throw new Error(`${out} was not written`)
|
|
68
|
+
run(['bun', 'x', '--bun', 'vsce', 'publish', '--packagePath', out])
|
|
69
|
+
console.log(`Published ${manifest.publisher}.${manifest.name}@${manifest.version}`)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* manifest, so the built-in stx support needs them declared here, pointing at
|
|
7
7
|
* dist/stx/ (copied there by build.ts). The library describes them in
|
|
8
8
|
* `contributes.json`; this derives the manifest from it rather than keeping a
|
|
9
|
-
* second copy by hand.
|
|
9
|
+
* second copy by hand. That includes the TypeScript server plugin entry, which
|
|
10
|
+
* type-checks `.stx` files (see ts-plugin.ts). Snippets are left out on purpose: the extension offers
|
|
10
11
|
* them as completions only when the stx extension is not installed, because
|
|
11
12
|
* a manifest entry would show every snippet twice next to it.
|
|
12
13
|
*
|
|
@@ -23,6 +24,7 @@ export interface StxContributes {
|
|
|
23
24
|
grammars: Array<Record<string, unknown> & { language: string, path: string }>
|
|
24
25
|
commands: Array<{ command: string, title: string, category?: string }>
|
|
25
26
|
configuration: { title: string, properties: Record<string, unknown> }
|
|
27
|
+
typescriptServerPlugins: Array<Record<string, unknown> & { name: string, languages?: string[] }>
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
const STX_DIR = './dist/stx/'
|
|
@@ -54,6 +56,7 @@ export function withStxContributes(manifest: any, stx: StxContributes): any {
|
|
|
54
56
|
grammars: stx.grammars.map(grammar => ({ ...grammar, path: fromStx(grammar.path) })),
|
|
55
57
|
commands: [...contributes.commands.filter((command: { command: string }) => command.command.startsWith(OWN_COMMAND_PREFIX)), ...stx.commands],
|
|
56
58
|
configuration: [...sections.filter(section => section.title !== stx.configuration.title), stx.configuration],
|
|
59
|
+
typescriptServerPlugins: stx.typescriptServerPlugins,
|
|
57
60
|
},
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stx TypeScript server plugin, as this extension ships it.
|
|
3
|
+
*
|
|
4
|
+
* It is what type-checks the TypeScript in `.stx` files (script blocks and
|
|
5
|
+
* template expressions) in the editor. `@stacksjs/stx-vscode` publishes it as a
|
|
6
|
+
* complete package, `dist/typescript-plugin/`, and its `contributes.json`
|
|
7
|
+
* carries the `typescriptServerPlugins` entry that `bun run sync:stx` copies
|
|
8
|
+
* into package.json.
|
|
9
|
+
*
|
|
10
|
+
* tsserver loads a plugin only by package name, resolved from
|
|
11
|
+
* `<extension>/node_modules`, the probe location VS Code passes for every
|
|
12
|
+
* extension that contributes one. This extension is packaged with
|
|
13
|
+
* `vsce --no-dependencies`, which leaves node_modules out of the VSIX, so:
|
|
14
|
+
*
|
|
15
|
+
* dist/stx-typescript-plugin/ the package itself, copied by build.ts
|
|
16
|
+
* (shipped through `files`, like the rest of dist/)
|
|
17
|
+
* node_modules/<name>/{package.json,index.js}
|
|
18
|
+
* a forwarder to it, written by build.ts for
|
|
19
|
+
* a development host and added to the VSIX
|
|
20
|
+
* by scripts/package.ts after vsce writes it
|
|
21
|
+
*
|
|
22
|
+
* The stx extension (Stacks.vscode-stx) contributes the same plugin under the
|
|
23
|
+
* same name. With both installed tsserver loads it twice, and the plugin
|
|
24
|
+
* decorates each project only the first time (stacksjs/stx#2028).
|
|
25
|
+
*/
|
|
26
|
+
import { cpSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
27
|
+
import { dirname, join } from 'node:path'
|
|
28
|
+
import { libraryContributes } from './stx-contributes'
|
|
29
|
+
|
|
30
|
+
/** Where build.ts puts the plugin package, relative to the extension root. */
|
|
31
|
+
export const TS_PLUGIN_DIST = 'dist/stx-typescript-plugin'
|
|
32
|
+
|
|
33
|
+
/** The name the plugin is contributed and configured under. */
|
|
34
|
+
export function tsPluginName(root: string): string {
|
|
35
|
+
const [plugin] = libraryContributes(root).typescriptServerPlugins
|
|
36
|
+
if (!plugin)
|
|
37
|
+
throw new Error('@stacksjs/stx-vscode contributes no TypeScript server plugin')
|
|
38
|
+
return plugin.name
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Where tsserver resolves the plugin from, relative to the extension root. */
|
|
42
|
+
export function tsPluginModuleDir(name: string): string {
|
|
43
|
+
return `node_modules/${name}`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The forwarder's files, relative to its directory under node_modules. */
|
|
47
|
+
export function tsPluginForwarder(name: string, version: string): Record<string, string> {
|
|
48
|
+
const up = '../'.repeat(1 + name.split('/').length)
|
|
49
|
+
return {
|
|
50
|
+
'package.json': `${JSON.stringify({ name, version, private: true, main: 'index.js' }, null, 2)}\n`,
|
|
51
|
+
// tsserver calls `module.exports` as the factory, unwrapped.
|
|
52
|
+
'index.js': `module.exports = require('${up}${TS_PLUGIN_DIST}/index.js')\n`,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Write the forwarder under `<root>/node_modules`, returning its directory. */
|
|
57
|
+
export function writeTsPluginForwarder(root: string, name: string, version: string): string {
|
|
58
|
+
const dir = join(root, tsPluginModuleDir(name))
|
|
59
|
+
mkdirSync(dir, { recursive: true })
|
|
60
|
+
for (const [file, content] of Object.entries(tsPluginForwarder(name, version)))
|
|
61
|
+
writeFileSync(join(dir, file), content)
|
|
62
|
+
return dir
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Copy the plugin package from `@stacksjs/stx-vscode` into `<root>/dist`. */
|
|
66
|
+
export function copyTsPlugin(root: string): string {
|
|
67
|
+
const source = dirname(Bun.resolveSync('@stacksjs/stx-vscode/typescript-plugin/package.json', root))
|
|
68
|
+
const target = join(root, TS_PLUGIN_DIST)
|
|
69
|
+
rmSync(target, { recursive: true, force: true })
|
|
70
|
+
cpSync(source, target, { recursive: true })
|
|
71
|
+
return target
|
|
72
|
+
}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* - ./stacks.ts preview, dev server and buddy commands
|
|
7
7
|
* - ./pickier.ts lint diagnostics, formatting and fix-all from the project's pickier
|
|
8
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
|
|
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
|
|
10
11
|
*
|
|
11
12
|
* Nothing here depends on another extension being installed.
|
|
12
13
|
*/
|
|
@@ -18,7 +19,7 @@ import { connect } from 'node:net'
|
|
|
18
19
|
import { join } from 'node:path'
|
|
19
20
|
import process from 'node:process'
|
|
20
21
|
import { createInterface } from 'node:readline'
|
|
21
|
-
import { activateStxLanguage, deactivateStxLanguage, STX_EXTENSION_ID } from '@stacksjs/stx-vscode'
|
|
22
|
+
import { activateStxLanguage, configureTypeScriptPlugin, deactivateStxLanguage, STX_EXTENSION_ID } from '@stacksjs/stx-vscode'
|
|
22
23
|
import * as vscode from 'vscode'
|
|
23
24
|
import { createEnvSupport, projectDecryptor } from './env'
|
|
24
25
|
import { createPickierSupport } from './pickier'
|
|
@@ -136,6 +137,15 @@ async function startStx(context: vscode.ExtensionContext): Promise<boolean> {
|
|
|
136
137
|
}
|
|
137
138
|
|
|
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
|
+
|
|
139
149
|
stacks.activate(context)
|
|
140
150
|
env.activate(context)
|
|
141
151
|
pickier = createPickierSupport(vscode, { exists: existsSync, spawnBun }, {
|
|
@@ -8,6 +8,8 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
8
8
|
import { builtinModules } from 'node:module'
|
|
9
9
|
import { join } from 'node:path'
|
|
10
10
|
import process from 'node:process'
|
|
11
|
+
import { TS_PLUGIN_DIST, tsPluginName } from '../scripts/ts-plugin'
|
|
12
|
+
import { expectDeclarationsFound, loadLikeTsserver } from './ts-plugin-harness'
|
|
11
13
|
|
|
12
14
|
const root = join(import.meta.dir, '..')
|
|
13
15
|
const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
|
|
@@ -39,6 +41,28 @@ describe('dist/extension.js', () => {
|
|
|
39
41
|
|
|
40
42
|
expect(unshipped).toEqual([])
|
|
41
43
|
})
|
|
44
|
+
|
|
45
|
+
it('starts the TypeScript extension and configures the stx plugin by the name the manifest contributes', () => {
|
|
46
|
+
const bundle = readFileSync(join(root, 'dist/extension.js'), 'utf8')
|
|
47
|
+
expect(bundle).toContain('vscode.typescript-language-features')
|
|
48
|
+
expect(bundle).toContain('configurePlugin')
|
|
49
|
+
for (const plugin of manifest.contributes.typescriptServerPlugins)
|
|
50
|
+
expect(bundle).toContain(plugin.name)
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('the stx TypeScript plugin', () => {
|
|
55
|
+
it('is in dist/ as the package the manifest contributes', () => {
|
|
56
|
+
const plugin = JSON.parse(readFileSync(join(root, TS_PLUGIN_DIST, 'package.json'), 'utf8'))
|
|
57
|
+
expect(manifest.contributes.typescriptServerPlugins.map((entry: { name: string }) => entry.name)).toEqual([plugin.name])
|
|
58
|
+
expect(manifest.files).toContain(`${TS_PLUGIN_DIST}/**`)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('loads from node_modules the way tsserver loads it, as in a development host', () => {
|
|
62
|
+
const name = tsPluginName(root)
|
|
63
|
+
expect(typeof loadLikeTsserver(root, name)).toBe('function')
|
|
64
|
+
expectDeclarationsFound(root, name, join(root, TS_PLUGIN_DIST, 'types'))
|
|
65
|
+
})
|
|
42
66
|
})
|
|
43
67
|
|
|
44
68
|
describe('dist/', () => {
|
|
@@ -92,7 +92,47 @@ describe('stx language support', () => {
|
|
|
92
92
|
})
|
|
93
93
|
})
|
|
94
94
|
|
|
95
|
+
// tsserver loads a plugin only by package name, from the extension's
|
|
96
|
+
// node_modules, and VS Code only sends it documents of a language a plugin
|
|
97
|
+
// claims (stacksjs/stx#2028).
|
|
98
|
+
describe('stx type checking', () => {
|
|
99
|
+
const plugins = manifest.contributes.typescriptServerPlugins as Array<{ name: string, languages?: string[], enableForWorkspaceTypeScriptVersions?: boolean }>
|
|
100
|
+
|
|
101
|
+
it('contributes the stx TypeScript server plugin by package name', () => {
|
|
102
|
+
expect(plugins.map(plugin => plugin.name)).toEqual(['@stacksjs/stx-typescript-plugin'])
|
|
103
|
+
for (const plugin of plugins) {
|
|
104
|
+
// The rule tsserver applies before loading anything (requestEnablePlugin).
|
|
105
|
+
expect(plugin.name).not.toMatch(/^(?:\.\.?(?:\/|$)|\/|[a-z]:)/i)
|
|
106
|
+
expect(plugin.name).not.toMatch(/[\\/]\.\.?(?:$|[\\/])/)
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('claims the stx language, also with the workspace TypeScript the project settings select', () => {
|
|
111
|
+
for (const plugin of plugins) {
|
|
112
|
+
expect(plugin.languages).toEqual(['stx'])
|
|
113
|
+
expect(plugin.enableForWorkspaceTypeScriptVersions).toBeTrue()
|
|
114
|
+
}
|
|
115
|
+
expect(manifest.contributes.configurationDefaults['typescript.tsdk']).toBe('node_modules/typescript/lib')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('declares the setting that switches it off', () => {
|
|
119
|
+
const settings = Object.assign({}, ...manifest.contributes.configuration.map((section: { properties: object }) => section.properties))
|
|
120
|
+
expect(settings['stxTypescriptPlugin.enabled'].default).toBeTrue()
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
95
124
|
describe('packaging', () => {
|
|
125
|
+
it('adds the plugin to the VSIX, which vsce --no-dependencies leaves without node_modules', () => {
|
|
126
|
+
expect(manifest.scripts.package).toBe('bun scripts/package.ts')
|
|
127
|
+
expect(manifest.scripts.release).toBe('bun scripts/package.ts --publish')
|
|
128
|
+
expect(readFileSync(join(root, 'scripts/package.ts'), 'utf8')).toContain(`'vsce', 'package', '--no-dependencies'`)
|
|
129
|
+
// The release workflow publishes through the script, not a bare `vsce publish`.
|
|
130
|
+
const workflow = readFileSync(join(root, '../../../../../.github/workflows/release.yml'), 'utf8')
|
|
131
|
+
const step = workflow.slice(workflow.indexOf('- name: Publish VS Code Extension'))
|
|
132
|
+
expect(step.slice(0, step.indexOf('- name:', 10))).toContain('bun run release')
|
|
133
|
+
expect(workflow).not.toContain('vsce publish')
|
|
134
|
+
})
|
|
135
|
+
|
|
96
136
|
it('ships the files the extension loads at runtime', () => {
|
|
97
137
|
for (const file of ['dist/extension.js', 'dist/pickier-worker.js', 'dist/pickier-worker.bunfig.toml'])
|
|
98
138
|
expect(manifest.files).toContain(file)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The VSIX, as VS Code installs it. It is packaged with `--no-dependencies`,
|
|
3
|
+
* which leaves node_modules out, so scripts/package.ts adds the stx TypeScript
|
|
4
|
+
* plugin's entry there afterwards; without it tsserver finds no plugin and no
|
|
5
|
+
* `.stx` file is type-checked.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, it } from 'bun:test'
|
|
8
|
+
import { mkdtempSync, readFileSync, realpathSync, rmSync } from 'node:fs'
|
|
9
|
+
import { tmpdir } from 'node:os'
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
import { packageExtension } from '../scripts/package'
|
|
12
|
+
import { TS_PLUGIN_DIST, tsPluginModuleDir, tsPluginName } from '../scripts/ts-plugin'
|
|
13
|
+
import { expectDeclarationsFound, loadLikeTsserver } from './ts-plugin-harness'
|
|
14
|
+
|
|
15
|
+
const root = join(import.meta.dir, '..')
|
|
16
|
+
const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
|
|
17
|
+
|
|
18
|
+
// vsce runs `vscode:prepublish` through npm, so this needs npm on PATH; the
|
|
19
|
+
// release workflow, which runs these tests before publishing, has it.
|
|
20
|
+
const canPackage = Boolean(Bun.which('npm') && Bun.which('zip') && Bun.which('unzip'))
|
|
21
|
+
|
|
22
|
+
describe('the VSIX', () => {
|
|
23
|
+
it.skipIf(!canPackage)('carries the stx TypeScript plugin, and it loads from the installed layout', () => {
|
|
24
|
+
// Real path: require() resolves symlinks, and macOS's tmpdir is one.
|
|
25
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), 'stacks-vsix-test-')))
|
|
26
|
+
try {
|
|
27
|
+
const vsix = packageExtension(join(dir, 'stacks.vsix'))
|
|
28
|
+
const listing = Bun.spawnSync(['unzip', '-Z1', vsix]).stdout.toString().split('\n')
|
|
29
|
+
const name = tsPluginName(root)
|
|
30
|
+
|
|
31
|
+
expect(listing).toContain(`extension/${tsPluginModuleDir(name)}/package.json`)
|
|
32
|
+
expect(listing).toContain(`extension/${tsPluginModuleDir(name)}/index.js`)
|
|
33
|
+
// Nothing else of node_modules: the bundle carries its dependencies.
|
|
34
|
+
expect(listing.filter(file => file.startsWith('extension/node_modules/') && !file.startsWith(`extension/${tsPluginModuleDir(name)}/`))).toEqual([])
|
|
35
|
+
expect(listing).toContain(`extension/${TS_PLUGIN_DIST}/types/stx-module.d.ts`)
|
|
36
|
+
|
|
37
|
+
expect(Bun.spawnSync(['unzip', '-q', vsix, 'extension/*', '-d', dir]).exitCode).toBe(0)
|
|
38
|
+
const installed = join(dir, 'extension')
|
|
39
|
+
expect(JSON.parse(readFileSync(join(installed, 'package.json'), 'utf8')).version).toBe(manifest.version)
|
|
40
|
+
expect(typeof loadLikeTsserver(installed, name)).toBe('function')
|
|
41
|
+
expectDeclarationsFound(installed, name, join(installed, TS_PLUGIN_DIST, 'types'))
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
rmSync(dir, { recursive: true, force: true })
|
|
45
|
+
}
|
|
46
|
+
}, 180_000)
|
|
47
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loading the stx TypeScript server plugin the way tsserver loads it, for the
|
|
3
|
+
* tests that check the built extension and the packaged VSIX.
|
|
4
|
+
*/
|
|
5
|
+
import { expect } from 'bun:test'
|
|
6
|
+
import { existsSync } from 'node:fs'
|
|
7
|
+
import { createRequire } from 'node:module'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `require()` the plugin by name from `<extensionRoot>/node_modules` (VS Code
|
|
12
|
+
* passes the extension root as the probe location) and take `module.exports`
|
|
13
|
+
* as the factory, unwrapped, as tsserver does.
|
|
14
|
+
*/
|
|
15
|
+
export function loadLikeTsserver(extensionRoot: string, name: string): unknown {
|
|
16
|
+
return createRequire(join(extensionRoot, 'node_modules', 'index.js'))(name)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create the plugin on a stub language service and read the buffer it serves
|
|
21
|
+
* for a `.stx` file: line 1 references the declaration files the plugin found,
|
|
22
|
+
* which it can only find when the package tells it where they are (Bun inlines
|
|
23
|
+
* `__dirname` into the bundle at build time).
|
|
24
|
+
*/
|
|
25
|
+
export function expectDeclarationsFound(extensionRoot: string, name: string, typesDir: string): void {
|
|
26
|
+
const factory = loadLikeTsserver(extensionRoot, name) as (modules: { typescript: unknown }) => { create: (info: unknown) => unknown }
|
|
27
|
+
expect(typeof factory).toBe('function')
|
|
28
|
+
|
|
29
|
+
const text = '<script client>\nconst n = state(0)\n</script>\n'
|
|
30
|
+
const snapshot = (value: string) => ({ getText: (start: number, end: number) => value.slice(start, end), getLength: () => value.length, getChangeRange: () => undefined })
|
|
31
|
+
const host: Record<string, any> = { getScriptSnapshot: () => snapshot(text), getScriptVersion: () => '1' }
|
|
32
|
+
factory({ typescript: { ScriptSnapshot: { fromString: snapshot } } })
|
|
33
|
+
.create({ languageService: {}, languageServiceHost: host, project: { projectService: { logger: { info: () => {} } } } })
|
|
34
|
+
|
|
35
|
+
const buffer = host.getScriptSnapshot('/tmp/page.stx')
|
|
36
|
+
const referenced = /^\/\/\/ <reference path="(.+)" \/>$/.exec(buffer.getText(0, buffer.getLength()).split('\n')[0])?.[1]
|
|
37
|
+
expect(referenced).toBe(join(typesDir, 'stx-module.d.ts'))
|
|
38
|
+
expect(existsSync(join(typesDir, 'stx.d.ts'))).toBeTrue()
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.48",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@iconify-json/f7": "^1.2.2",
|
|
57
57
|
"@iconify-json/hugeicons": "^1.2.35",
|
|
58
|
-
"@stacksjs/mobile": "^0.75.
|
|
58
|
+
"@stacksjs/mobile": "^0.75.48",
|
|
59
59
|
"@stacksjs/sanitizer": "^0.2.348",
|
|
60
60
|
"ts-qr-codes": "^0.1.8"
|
|
61
61
|
}
|