Skip to content

Commit e394e61

Browse files
author
Orta Therox
authored
Merge pull request #1025 from microsoft/deploy_types
Add the ability to upload to @types/x
2 parents aad7832 + 41abedf commit e394e61

File tree

9 files changed

+352
-0
lines changed

9 files changed

+352
-0
lines changed

.github/workflows/deploy.yml

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Deploy to npm
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
workflow_dispatch:
9+
10+
jobs:
11+
deploy:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
- uses: actions/setup-node@v1
17+
with:
18+
node-version: "15.x"
19+
registry-url: "https://registry.npmjs.org"
20+
21+
- run: npm install
22+
- run: npm run build
23+
- run: npm test
24+
25+
- name: Create packages for .d.ts files
26+
run: node deploy/createTypesPackages.mjs
27+
28+
# Deploy anything which differs from the npm version of a tsconfig
29+
- name: 'Deploy built packages to NPM'
30+
run: node deploy/deployChangedPackages.mjs
31+
env:
32+
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34+

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,4 @@ inputfiles/browser.webidl.json
286286
package-lock.json
287287
yarn.lock
288288
TypeScript
289+
deploy/generated

deploy/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Deploys
2+
3+
We want to generate @types/xyz

deploy/createTypesPackages.mjs

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// @ts-check
2+
3+
// node deploy/createTypesPackages.mjs
4+
5+
// prettier-ignore
6+
const packages = [
7+
{
8+
name: "@types/web",
9+
description: "Types for the DOM, and other web technologies in browsers",
10+
readme: "./readmes/web.md",
11+
files: [
12+
{ from: "../generated/dom.generated.d.ts", to: "index.d.ts" },
13+
{ from: "../generated/dom.iterable.generated.d.ts", to: "index.iterable.d.ts" }
14+
],
15+
},
16+
];
17+
18+
// Note: You can add 'version: "1.0.0"' to a package above
19+
// to set the major or minor, otherwise it will always bump
20+
// the patch.
21+
22+
import { join, dirname } from "path";
23+
import fs from "fs";
24+
import { fileURLToPath } from "url";
25+
import semver from "semver";
26+
import pkg from "prettier";
27+
const { format } = pkg;
28+
import { execSync } from "child_process";
29+
30+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
31+
// @ts-ignore
32+
const __filename = fileURLToPath(import.meta.url);
33+
const __dirname = dirname(__filename);
34+
35+
const go = async () => {
36+
const gitSha = execSync("git rev-parse HEAD").toString().trim().slice(0, 7);
37+
38+
const generatedDir = join(__dirname, "generated");
39+
const templateDir = join(__dirname, "template");
40+
41+
for (const pkg of packages) {
42+
const folderName = pkg.name.replace("@", "").replace("/", "-");
43+
const packagePath = join(generatedDir, folderName);
44+
45+
if (fs.existsSync(packagePath)) fs.rmSync(packagePath, { recursive: true });
46+
fs.mkdirSync(packagePath, { recursive: true });
47+
48+
// Migrate in the template files
49+
for (const templateFile of fs.readdirSync(templateDir)) {
50+
if (templateFile.startsWith(".")) continue;
51+
52+
const templatedFile = join(templateDir, templateFile);
53+
fs.copyFileSync(templatedFile, join(packagePath, templateFile));
54+
}
55+
56+
// Add the reference files in the config above
57+
pkg.files.forEach((fileRef) => {
58+
fs.copyFileSync(
59+
join(__filename, "..", fileRef.from),
60+
join(packagePath, fileRef.to)
61+
);
62+
});
63+
64+
// Setup the files in the repo
65+
const newPkgJSON = await updatePackageJSON(packagePath, pkg, gitSha);
66+
copyREADME(pkg, newPkgJSON, join(packagePath, "README.md"));
67+
68+
// Done
69+
console.log("Built:", pkg.name);
70+
}
71+
};
72+
73+
go();
74+
75+
async function updatePackageJSON(packagePath, pkg, gitSha) {
76+
const pkgJSONPath = join(packagePath, "package.json");
77+
const packageText = fs.readFileSync(pkgJSONPath, "utf8");
78+
const packageJSON = JSON.parse(packageText);
79+
packageJSON.name = pkg.name;
80+
packageJSON.description = pkg.description;
81+
82+
// Bump the last version of the number from npm,
83+
// or use the _version in tsconfig if it's higher,
84+
// or default to 0.0.1
85+
let version = pkg.version || "0.0.1";
86+
try {
87+
const npmResponse = await fetch(
88+
`https://registry.npmjs.org/${packageJSON.name}`
89+
);
90+
const npmPackage = await npmResponse.json();
91+
92+
const semverMarkers = npmPackage["dist-tags"].latest.split(".");
93+
const bumpedVersion = `${semverMarkers[0]}.${semverMarkers[1]}.${
94+
Number(semverMarkers[2]) + 1
95+
}`;
96+
97+
if (semver.gt(version, bumpedVersion)) {
98+
version = bumpedVersion;
99+
}
100+
} catch (error) {
101+
// NOOP, this is for the first deploy, which will set it to 0.0.1
102+
}
103+
104+
packageJSON.version = version;
105+
packageJSON.domLibGeneratorSha = gitSha;
106+
107+
fs.writeFileSync(
108+
pkgJSONPath,
109+
format(JSON.stringify(packageJSON), { filepath: pkgJSONPath })
110+
);
111+
112+
return packageJSON;
113+
}
114+
115+
// Copies the README and adds some rudimentary templating to the file.
116+
function copyREADME(pkg, pkgJSON, writePath) {
117+
let readme = fs.readFileSync(join(__filename, "..", pkg.readme), "utf-8");
118+
119+
const htmlEncodedTag =
120+
encodeURIComponent(pkgJSON.name) + "%40" + pkgJSON.version;
121+
122+
readme = readme
123+
.replace("{{version}}", pkgJSON.version)
124+
.replace(
125+
"{{release_href}}",
126+
`https://github.com/microsoft/TypeScript-DOM-lib-generator/releases/tag/${htmlEncodedTag}`
127+
);
128+
129+
fs.writeFileSync(writePath, readme);
130+
}

deploy/deployChangedPackages.mjs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// @ts-check
2+
3+
// node deploy/deployChangedPackages.mjs
4+
5+
// Builds on the results of createTypesPackages.mjs and deploys the
6+
// ones which have changed.
7+
8+
import * as fs from "fs";
9+
import { join, dirname } from "path";
10+
import { fileURLToPath } from "url";
11+
import fetch from "node-fetch";
12+
import { spawnSync } from "child_process";
13+
import { Octokit } from "@octokit/core";
14+
15+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
16+
// @ts-ignore
17+
const __filename = fileURLToPath(import.meta.url);
18+
const __dirname = dirname(__filename);
19+
20+
const verify = () => {
21+
const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN;
22+
if (!authToken)
23+
throw new Error(
24+
"There isn't an ENV var set up for creating a GitHub release, expected GITHUB_TOKEN."
25+
);
26+
};
27+
28+
const go = async () => {
29+
verify();
30+
31+
const uploaded = [];
32+
33+
// Loop through generated packages, deploying versions for anything which has different
34+
// .d.ts files from the version available on npm.
35+
const generatedDir = join(__dirname, "generated");
36+
for (const dirName of fs.readdirSync(generatedDir)) {
37+
const localPackageJSONPath = join(generatedDir, dirName, "package.json");
38+
const newTSConfig = fs.readFileSync(localPackageJSONPath, "utf-8");
39+
const pkgJSON = JSON.parse(newTSConfig);
40+
41+
const dtsFiles = fs
42+
.readdirSync(join(generatedDir, dirName))
43+
.filter((f) => f.endsWith(".d.ts"));
44+
45+
// Look through each .d.ts file included in a package to
46+
// determine if anything has changed
47+
let upload = false;
48+
for (const file of dtsFiles) {
49+
const generatedDTSPath = join(generatedDir, dirName, file);
50+
const generatedDTSContent = fs.readFileSync(generatedDTSPath, "utf8");
51+
const unpkgURL = `https://unpkg.com/${pkgJSON.name}/${file}`;
52+
try {
53+
const npmDTSReq = await fetch(unpkgURL);
54+
const npmDTSText = await npmDTSReq.text();
55+
upload = upload || npmDTSText !== generatedDTSContent;
56+
} catch (error) {
57+
// Could not find a previous build
58+
console.log(`
59+
Could not get the file ${file} inside the npm package ${pkgJSON.name} from unpkg at ${unpkgURL}
60+
Assuming that this means we need to upload this package.`);
61+
upload = true;
62+
}
63+
}
64+
65+
// Publish via npm
66+
if (upload) {
67+
if (process.env.NODE_AUTH_TOKEN) {
68+
const publish = spawnSync("npm", ["publish", "--access", "public"], {
69+
cwd: join("packages", dirName),
70+
});
71+
72+
if (publish.status) {
73+
console.log(publish.stdout?.toString());
74+
console.log(publish.stderr?.toString());
75+
process.exit(publish.status);
76+
} else {
77+
console.log(publish.stdout?.toString());
78+
79+
await createRelease(`${pkgJSON.name}@${pkgJSON.version}`);
80+
}
81+
}
82+
83+
uploaded.push(dirName);
84+
}
85+
}
86+
87+
// Warn if we did a dry run.
88+
if (!process.env.NODE_AUTH_TOKEN) {
89+
console.log(
90+
"Did a dry run because process.env.NODE_AUTH_TOKEN is not set."
91+
);
92+
}
93+
94+
if (uploaded.length) {
95+
console.log("Uploaded: ", uploaded.join(", "));
96+
} else {
97+
console.log("No uploads");
98+
}
99+
};
100+
101+
async function createRelease(tag) {
102+
const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN;
103+
const octokit = new Octokit({ auth: authToken });
104+
105+
await octokit.request("POST /repos/{owner}/{repo}/releases", {
106+
owner: "microsoft",
107+
repo: "TypeScript-DOM-lib-generator",
108+
tag_name: tag,
109+
target_commitish: process.env.GITHUB_SHA,
110+
});
111+
}
112+
113+
go();

deploy/readmes/web.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
### `@types/web` - Types for the DOM and most web-related APIs
2+
3+
This module contains the DOM types for the majority of the web APIs used in a web browser.
4+
5+
The APIs inside `@types/web` are generated from the specifications for CSS, HTML and JavaScript. Given the size and state of constant change in web browsers, `@types/web` only has APIs which have passed a certain level of standardization and are available in at least two different browser engines.
6+
7+
`@types/web` is also included inside TypeScript, available as `dom` in the [`lib`](https://www.typescriptlang.org/tsconfig#lib) section and included in projects by default. By using `@types/web` you can lock your the web APIs used in your projects, easing the process of updating TypeScript and offering more control in your environment.
8+
9+
## Installation
10+
11+
To use `@types/web` you need to do two things:
12+
13+
1. Install the dependency: `npm install @types/web --save-dev`, `yarn add @types/web --dev` or `pnpm add @types/web --dev`.
14+
15+
1. Update your [`tsconfig.json`](https://www.typescriptlang.org/tsconfig). There are two cases to consider depending on if you have `lib` defined in your `tsconfig.json` or not.
16+
17+
1. **Without "lib"** - You will need to add `"lib": []`. The value you want to add inside your lib should correlate to your [`"target"`](https://www.typescriptlang.org/tsconfig#target). For example if you had `"target": "es2017"`, then you would add `"lib": ["es2017"]`
18+
1. **With "lib"** - You should remove `"dom"`.
19+
20+
That's all.
21+
22+
## SemVer
23+
24+
This project does not respect semantic versioning as almost every change could potentially break a project, though we try to minimize removing types.
25+
`@types/web` follow the specifications, so when they mark a function/object/API/type as deprecated or removed - that is respected.
26+
27+
## TypeScript Version Support
28+
29+
Prior to `@types/web` the web APIs were deployed with a version of TypeScript, and backwards compatibility has not been a concern. Now the web APIs and TypeScript can be de-coupled, then we expect to eventually hit a point where we take backwards compatibility in mind. For now, `@types/web` officially supports TypeScript 4.4 and above. It very likely will work with TypeScript versions much earlier that that however.
30+
31+
## Deploy Metadata
32+
33+
You can read what changed in version {{version}} at {{release_href}}.

deploy/template/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) Microsoft Corporation.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE

deploy/template/package.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "@types/xyz",
3+
"version": "0.0.x",
4+
"description": "TypeScript definitions for xyz",
5+
"license": "MIT",
6+
"contributors": [],
7+
"main": "",
8+
"types": "index.d.ts",
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/microsoft/TypeScript-DOM-Lib-Generator.git"
12+
},
13+
"scripts": {},
14+
"dependencies": {}
15+
}

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
},
1818
"dependencies": {
1919
"@mdn/browser-compat-data": "2.0.7",
20+
"@octokit/core": "^3.5.1",
2021
"@types/jsdom": "^16.2.10",
2122
"@types/node": "^15.6.1",
2223
"@types/node-fetch": "^2.5.10",
@@ -33,6 +34,7 @@
3334
"parse-diff": "^0.8.1",
3435
"prettier": "^2.3.0",
3536
"print-diff": "^1.0.0",
37+
"semver": "^7.3.5",
3638
"styleless-innertext": "^1.1.3",
3739
"typescript": "^4.3.0-dev.20210327",
3840
"webidl2": "^24.1.1"

0 commit comments

Comments
 (0)