Skip to content

Commit 6f68df1

Browse files
Copilotedvilme
andauthored
Apply remaining changes
Co-authored-by: edvilme <5952839+edvilme@users.noreply.github.com>
2 parents a2409d1 + 20c1381 commit 6f68df1

118 files changed

Lines changed: 4138 additions & 7461 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/pr-file-check.yml‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,21 @@ jobs:
4545
- name: 'Public API changes require a version bump'
4646
uses: brettcannon/check-for-changed-files@d85c64d17b3c1d0ac57c9cc46ea39399b0d6fa71 # v1.2.2
4747
with:
48-
prereq-pattern: 'src/api.ts'
48+
prereq-pattern: |
49+
src/api.ts
50+
src/types.ts
51+
src/publicErrors.ts
4952
file-pattern: 'api/package.json'
5053
skip-label: 'skip api version'
5154
failure-message: 'The public API (${prereq-pattern}) was changed without bumping the package version in ${file-pattern} (the ${skip-label} label can be used to pass this check)'
5255

5356
- name: 'Public API changes require a changelog entry'
5457
uses: brettcannon/check-for-changed-files@d85c64d17b3c1d0ac57c9cc46ea39399b0d6fa71 # v1.2.2
5558
with:
56-
prereq-pattern: 'src/api.ts'
59+
prereq-pattern: |
60+
src/api.ts
61+
src/types.ts
62+
src/publicErrors.ts
5763
file-pattern: 'api/CHANGELOG.md'
5864
skip-label: 'skip api changelog'
5965
failure-message: 'The public API (${prereq-pattern}) was changed without a changelog entry in ${file-pattern} (the ${skip-label} label can be used to pass this check)'

‎CONTRIBUTING.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
9191

9292
## Public API package (`@vscode/python-environments`)
9393

94-
The npm package under [`api/`](./api) is the public API facade other extensions consume. Its entry point, `api/src/main.ts`, is a **copy** of [`src/api.ts`](./src/api.ts) — the single source of truth — and is **not committed** (see [`api/.gitignore`](./api/.gitignore)).
94+
The npm package under [`api/`](./api) is the public API facade other extensions consume. Its sources — `api/src/main.ts`, `api/src/types.ts`, and `api/src/publicErrors.ts` — are **copies** of [`src/api.ts`](./src/api.ts), [`src/types.ts`](./src/types.ts), and [`src/publicErrors.ts`](./src/publicErrors.ts) respectively — the single sources of truth — and are **not committed** (see [`api/.gitignore`](./api/.gitignore)).
9595

96-
- Edit the API only in `src/api.ts`. This file contains the full public surface, including the runtime `PythonEnvironments.api()` helper and `EXTENSION_ID`. `api/src/main.ts` is a build artifact — never edit or commit it.
97-
- `api/src/main.ts` is produced by the publish pipeline ([`build/azure-pipeline.npm.yml`](./build/azure-pipeline.npm.yml)), which copies `src/api.ts` to `api/src/main.ts` before compiling. The api package is therefore built in CI only; to build it locally, copy the file first (e.g. `cp src/api.ts api/src/main.ts`).
98-
- `src/api.ts` itself is validated on every PR by the extension's own lint and TypeScript compile.
99-
- **Versioning:** the published package version in [`api/package.json`](./api/package.json) must always match the extension version in [`package.json`](./package.json). CI enforces this via [`scripts/compare_package_versions.py`](./scripts/compare_package_versions.py). Additionally, any PR that edits `src/api.ts` must bump `api/package.json` (use the `skip api version` label to bypass) and add an entry to [`api/CHANGELOG.md`](./api/CHANGELOG.md) (use the `skip api changelog` label to bypass). When bumping, update both `package.json` files so they stay in sync.
96+
- Edit the public API only in `src/api.ts` (the runtime facade: `PythonEnvironments.api()` helper and `EXTENSION_ID`), `src/types.ts` (public contracts: interfaces, types, enums), and `src/publicErrors.ts` (concrete public error classes and type guards). `api/src/*.ts` files are build artifacts — never edit or commit them.
97+
- `api/src/main.ts`, `api/src/types.ts`, and `api/src/publicErrors.ts` are produced by the publish pipeline ([`build/azure-pipeline.npm.yml`](./build/azure-pipeline.npm.yml)), which copies `src/api.ts` to `api/src/main.ts`, `src/types.ts` to `api/src/types.ts`, and `src/publicErrors.ts` to `api/src/publicErrors.ts` before compiling. The api package is therefore built in CI only; to build it locally, copy the files first (e.g. `cp src/api.ts api/src/main.ts && cp src/types.ts api/src/types.ts && cp src/publicErrors.ts api/src/publicErrors.ts`).
98+
- `src/api.ts`, `src/types.ts`, and `src/publicErrors.ts` are validated on every PR by the extension's own lint and TypeScript compile.
99+
- **Versioning and compatibility:** the published package version in [`api/package.json`](./api/package.json) is maintained independently of the extension version in [`package.json`](./package.json) — the two do not need to match. Compatibility is based on the API shape exported by the installed Python Environments extension at runtime. Package updates must preserve backwards-compatible contracts unless the API package version intentionally communicates a breaking change; consumers should treat newly added members as optional when they may run against older installed extension versions. Any PR that edits `src/api.ts`, `src/types.ts`, or `src/publicErrors.ts` must bump `api/package.json` (use the `skip api version` label to bypass) and add an entry to [`api/CHANGELOG.md`](./api/CHANGELOG.md) (use the `skip api changelog` label to bypass).
100100

101101
## Questions or Issues?
102102

‎README.md‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,7 @@ The Python Environments extension was built to provide a cohesive and user frien
155155

156156
### API Reference (proposed)
157157

158-
See [api.ts](https://github.com/microsoft/vscode-python-environments/blob/main/src/api.ts) for the full list of Extension APIs.
159-
160-
To consume these APIs you can look at the example here: [API Consumption Examples](https://github.com/microsoft/vscode-python-environments/blob/main/examples/README.md)
158+
See [api.ts](https://github.com/microsoft/vscode-python-environments/blob/main/src/api.ts) for the runtime API facade and [types.ts](https://github.com/microsoft/vscode-python-environments/blob/main/src/types.ts) for the public API contracts. Extension authors can consume these contracts from the `@vscode/python-environments` npm package.
161159

162160
### Callable Commands
163161

‎api/.gitignore‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
# Copied from ../src/api.ts by the publish pipeline (build/azure-pipeline.npm.yml).
2-
# This is the published package entry point; it is produced at publish time and
3-
# intentionally NOT committed. src/api.ts is the single source of truth.
1+
# Copied from ../src/api.ts, ../src/types.ts, ../src/publicErrors.ts by the publish pipeline
2+
# (build/azure-pipeline.npm.yml). These are the published package sources; they are produced
3+
# at publish time and intentionally NOT committed. src/api.ts, src/types.ts, and
4+
# src/publicErrors.ts are the single sources of truth.
45
src/main.ts
6+
src/types.ts
7+
src/publicErrors.ts

‎api/CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to the `@vscode/python-environments` API package are documen
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.4.0]
9+
10+
### Changed
11+
12+
- Reorganized the package source into separate API facade, public contract, and public error modules without changing the root package exports.
13+
814
## [1.3.0]
915

1016
### Added

‎api/package-lock.json‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎api/package.json‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@vscode/python-environments",
33
"description": "An API facade for the Python Environments extension in VS Code",
4-
"version": "1.3.0",
4+
"version": "1.4.0",
55
"author": {
66
"name": "Microsoft Corporation"
77
},
@@ -12,14 +12,14 @@
1212
"Environments"
1313
],
1414
"main": "./out/cjs/main.cjs",
15-
"types": "./out/cjs/main.d.ts",
15+
"types": "./out/types/main.d.ts",
1616
"exports": {
1717
"import": {
18-
"types": "./out/esm/main.d.ts",
18+
"types": "./out/types/main.d.ts",
1919
"default": "./out/esm/main.mjs"
2020
},
2121
"require": {
22-
"types": "./out/cjs/main.d.ts",
22+
"types": "./out/types/main.d.ts",
2323
"default": "./out/cjs/main.cjs"
2424
}
2525
},
@@ -39,9 +39,11 @@
3939
"scripts": {
4040
"prepublishOnly": "echo \"⛔ Can only publish from a secure pipeline ⛔\" && node -e \"process.exitCode = 1\"",
4141
"prepack": "npm run all:publish",
42-
"all:publish": "git clean -xfd . && npm install && npm run compile",
43-
"compile": "npm run compile:esm && npm run compile:cjs",
44-
"compile:esm": "tsc -b ./tsconfig.esm.json && mve out/esm/main.js out/esm/main.mjs",
42+
"all:publish": "git clean -xfd . && npm install && npm run copy:sources && npm run compile",
43+
"copy:sources": "node ./scripts/copy-sources.cjs",
44+
"compile": "npm run clean && npm run compile:types && npm run compile:esm && npm run compile:cjs",
45+
"compile:types": "tsc -b ./tsconfig.types.json",
46+
"compile:esm": "tsc -b ./tsconfig.esm.json && mve out/esm/main.js out/esm/main.mjs && node -e \"require('fs').writeFileSync('out/esm/package.json', '{\\\"type\\\":\\\"module\\\"}')\"",
4547
"compile:cjs": "tsc -b ./tsconfig.cjs.json && mve out/cjs/main.js out/cjs/main.cjs",
4648
"clean": "node -e \"const fs = require('fs'); fs.rmSync('./out', { recursive: true, force: true });\"",
4749
"test:package": "node ./scripts/test-package.cjs"

‎api/scripts/copy-sources.cjs‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
// Repopulates the package's generated `src` sources from the single sources of
5+
// truth in `../src`. These files are gitignored and are removed by the
6+
// `git clean -xfd .` step in `all:publish`, so they must be copied back before
7+
// TypeScript can compile. Mirrors the copy step in build/azure-pipeline.npm.yml.
8+
9+
const fs = require('node:fs');
10+
const path = require('node:path');
11+
12+
const packageRoot = path.resolve(__dirname, '..');
13+
const repoRoot = path.resolve(packageRoot, '..');
14+
const srcDir = path.join(packageRoot, 'src');
15+
16+
const sources = [
17+
{ from: path.join(repoRoot, 'src', 'api.ts'), to: path.join(srcDir, 'main.ts') },
18+
{ from: path.join(repoRoot, 'src', 'types.ts'), to: path.join(srcDir, 'types.ts') },
19+
{ from: path.join(repoRoot, 'src', 'publicErrors.ts'), to: path.join(srcDir, 'publicErrors.ts') },
20+
];
21+
22+
fs.mkdirSync(srcDir, { recursive: true });
23+
for (const { from, to } of sources) {
24+
fs.copyFileSync(from, to);
25+
}

‎api/scripts/test-package.cjs‎

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,33 @@ try {
7878
}
7979

8080
const installedPackageRoot = path.join(testRoot, 'node_modules', '@vscode', 'python-environments');
81+
const vscodeStubRoot = path.join(testRoot, 'node_modules', 'vscode');
82+
fs.mkdirSync(vscodeStubRoot, { recursive: true });
83+
fs.writeFileSync(path.join(vscodeStubRoot, 'package.json'), JSON.stringify({ main: 'index.js' }));
84+
fs.writeFileSync(
85+
path.join(vscodeStubRoot, 'index.js'),
86+
[
87+
"const runtimeApi = { getEnvironments: async () => [] };",
88+
"const extension = {",
89+
" isActive: false,",
90+
" exports: undefined,",
91+
" packageJSON: { version: '1.37.0' },",
92+
" activate: async () => { extension.isActive = true; extension.exports = runtimeApi; return runtimeApi; },",
93+
"};",
94+
'exports.__runtimeApi = runtimeApi;',
95+
'exports.extensions = { getExtension: () => extension };',
96+
].join('\n'),
97+
);
8198
const installedPackageJson = JSON.parse(fs.readFileSync(path.join(installedPackageRoot, 'package.json'), 'utf8'));
8299
assert.strictEqual(installedPackageJson.main, './out/cjs/main.cjs');
83-
assert.strictEqual(installedPackageJson.types, './out/cjs/main.d.ts');
100+
assert.strictEqual(installedPackageJson.types, './out/types/main.d.ts');
84101
assert.deepStrictEqual(installedPackageJson.exports, {
85102
import: {
86-
types: './out/esm/main.d.ts',
103+
types: './out/types/main.d.ts',
87104
default: './out/esm/main.mjs',
88105
},
89106
require: {
90-
types: './out/cjs/main.d.ts',
107+
types: './out/types/main.d.ts',
91108
default: './out/cjs/main.cjs',
92109
},
93110
});
@@ -102,26 +119,65 @@ try {
102119
]) {
103120
assert.ok(fs.statSync(path.resolve(installedPackageRoot, target)).isFile(), `${target} must be a file`);
104121
}
122+
for (const runtimeOutput of ['esm', 'cjs']) {
123+
const runtimeOutputRoot = path.join(installedPackageRoot, 'out', runtimeOutput);
124+
const duplicateDeclarations = fs
125+
.readdirSync(runtimeOutputRoot, { recursive: true })
126+
.filter((entry) => entry.endsWith('.d.ts'));
127+
assert.deepStrictEqual(
128+
duplicateDeclarations,
129+
[],
130+
`${runtimeOutputRoot} must not contain declaration files: ${duplicateDeclarations.join(', ')}`,
131+
);
132+
}
105133

106134
const requireFromConsumer = createRequire(path.join(testRoot, 'legacy', 'consumer.cjs'));
135+
const commonJsModule = requireFromConsumer('@vscode/python-environments');
136+
assert.strictEqual(
137+
typeof commonJsModule.PythonEnvironments.api,
138+
'function',
139+
'CommonJS consumers should load the package runtime facade',
140+
);
141+
execFileSync(
142+
process.execPath,
143+
[
144+
'--eval',
145+
[
146+
"const packageModule = require('@vscode/python-environments');",
147+
"const vscode = require('vscode');",
148+
"(async () => {",
149+
' const api = await packageModule.PythonEnvironments.api();',
150+
' if (api !== vscode.__runtimeApi) process.exit(1);',
151+
'})().catch(() => process.exit(1));',
152+
].join('\n'),
153+
],
154+
{
155+
cwd: path.join(testRoot, 'legacy'),
156+
encoding: 'utf8',
157+
},
158+
);
107159
assert.strictEqual(
108160
canonicalPath(requireFromConsumer.resolve('@vscode/python-environments')),
109161
canonicalPath(path.join(installedPackageRoot, installedPackageJson.exports.require.default)),
110162
'CommonJS consumers should resolve the packaged CommonJS entry point',
111163
);
112164

113-
const esmEntryPoint = execFileSync(
165+
const esmModuleCheck = execFileSync(
114166
process.execPath,
115-
['--input-type=module', '--eval', "console.log(import.meta.resolve('@vscode/python-environments'))"],
167+
[
168+
'--input-type=module',
169+
'--eval',
170+
"const packageModule = await import('@vscode/python-environments'); const vscode = await import('vscode'); if (typeof packageModule.PythonEnvironments.api !== 'function') process.exit(1); const api = await packageModule.PythonEnvironments.api(); if (api !== vscode.default.__runtimeApi) process.exit(1); console.log(import.meta.resolve('@vscode/python-environments'));",
171+
],
116172
{
117173
cwd: path.join(testRoot, 'modern'),
118174
encoding: 'utf8',
119175
},
120176
).trim();
121177
assert.strictEqual(
122-
canonicalPath(fileURLToPath(esmEntryPoint)),
178+
canonicalPath(fileURLToPath(esmModuleCheck)),
123179
canonicalPath(path.join(installedPackageRoot, installedPackageJson.exports.import.default)),
124-
'ES module consumers should resolve the packaged ES module entry point',
180+
'ES module consumers should load the packaged runtime facade',
125181
);
126182
} finally {
127183
fs.rmSync(testRoot, { recursive: true, force: true });

‎api/test/consumer.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import type {
22
PackageManager,
33
Pep440Version,
44
PythonEnvironment,
5+
PythonEnvironmentApi,
56
PythonPackageGetterApi,
67
} from '@vscode/python-environments';
78
import {
89
isPackageVersionLookupNotSupportedError,
910
PackageVersionLookupNotSupportedError,
11+
PythonEnvironments,
1012
} from '@vscode/python-environments';
1113

1214
type Equal<Left, Right> =
@@ -32,6 +34,7 @@ const explicitLegacyAvailableVersions: Promise<Pep440Version[] | undefined> = ap
3234
const throwingAvailableVersions: Promise<Pep440Version[]> = api.getPackageAvailableVersions(environment, 'example', {
3335
errorMode: 'throw',
3436
});
37+
const runtimeApi: Promise<PythonEnvironmentApi> = PythonEnvironments.api();
3538

3639
// The unsupported-capability error is part of the public contract: it is constructible, extends
3740
// Error, and exposes a stable string-literal `code` discriminator.
@@ -50,6 +53,7 @@ void refreshReturnIsExact;
5053
void legacyAvailableVersions;
5154
void explicitLegacyAvailableVersions;
5255
void throwingAvailableVersions;
56+
void runtimeApi;
5357
void lookupErrorIsError;
5458
void lookupErrorCodeIsExact;
5559
void guardNarrows;

0 commit comments

Comments
 (0)