Skip to content

Documents the release to TS process, and adds support for easily generating a CHANGELOG for 2 arbitrary versions #1057

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ This tool is used to generate the web-based `lib.dom.d.ts` file which is include

## Why is my fancy API still not available here?

A feature needs to be supported by more than two major browser engines to be included here, to make sure there is a good consensus among vendors: __Gecko__ (Firefox), __Blink__ (Chrome/Edge), and __WebKit__ (Safari).
A feature needs to be supported by two or more major browser engines to be included here, to make sure there is a good consensus among vendors: __Gecko__ (Firefox), __Blink__ (Chrome/Edge), and __WebKit__ (Safari).

If the condition is met but still is not available here, please [file an issue](hthttps://github.com/microsoft/TypeScript-DOM-lib-generator/issues/new).
If the condition is met but still is not available here, first check the heuristics below and then please [file an issue](hthttps://github.com/microsoft/TypeScript-DOM-lib-generator/issues/new).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need that heuristics anymore. If something is supported well by browsers then it should be supported here, there is no good reason not to do so IMO.


## Build Instructions

Expand All @@ -28,13 +28,13 @@ To test:
npm run test
```

To deploy:

```sh
npm run migrate
```
## `@types/[lib]` to TypeScript Versions

The script will look in for a clone of the TypeScript repo in "../TypeScript", or "./TypeScript" to move the generated files in.
| `@types/[lib]` version | TypeScript Version | Minimum TypeScript Support |
| ---------------------------------------------------------------------- | ----------- | -------------- |
| `@types/web` [0.0.1](https://www.npmjs.com/package/@types/web/v/0.0.1) | ~4.3 | 4.4 |
| `@types/web` [0.0.2](https://www.npmjs.com/package/@types/web/v/0.0.2) | ~4.4 beta | 4.4 |

## Contribution Guidelines

Expand Down Expand Up @@ -116,3 +116,26 @@ To give you a sense of whether we will accept changes, you can use these heurist
- `removedTypes.json`: types that are defined in the spec file but should be removed.
- `comments.json`: comment strings to be embedded in the generated .js files.
- `deprecatedMessage.json`: the reason why one type is deprecated. The reason why it is a separate file rather than merge in comment.json is mdn/apiDescriptions.json would also possibly be deprecated.

## Deployment to TypeScript

To migrate the *.d.ts files into TypeScript:

1. Run:

```sh
npm run migrate -- [previous_types_web_version]
```

The script will look in for a clone of the TypeScript repo in "../TypeScript", or "./TypeScript" to move the generated files in. Or migrate the files manually, you do you.

1. Update the README table with the mappings for versions in the `@types/[lib]`. E.g. TS 4.5 -> `@types/web` `0.0.23`.

1. Generate a CHANGELOG for the releases:

```sh
# lib from to
npm run ts-changelog -- @types/web 0.0.2 0.0.23
```

1. Add the CHANGELOG to the release issue
8 changes: 5 additions & 3 deletions deploy/createTypesPackages.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// node deploy/createTypesPackages.mjs

// prettier-ignore
const packages = [
export const packages = [
{
name: "@types/web",
description: "Types for the DOM, and other web technologies in browsers",
Expand Down Expand Up @@ -71,8 +71,6 @@ const go = async () => {
}
};

go();

async function updatePackageJSON(packagePath, pkg, gitSha) {
const pkgJSONPath = join(packagePath, "package.json");
const packageText = fs.readFileSync(pkgJSONPath, "utf8");
Expand Down Expand Up @@ -131,3 +129,7 @@ function copyREADME(pkg, pkgJSON, writePath) {

fs.writeFileSync(writePath, readme);
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
go();
}
55 changes: 43 additions & 12 deletions deploy/deployChangedPackages.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
// ones which have changed.

import * as fs from "fs";
import { join, dirname } from "path";
import { join, dirname, basename } from "path";
import { fileURLToPath } from "url";
import fetch from "node-fetch";
import { spawnSync } from "child_process";
import { spawnSync, execSync } from "child_process";
import { Octokit } from "@octokit/core";
import printDiff from "print-diff";
import { generateChangelogFrom } from "../lib/changelog.js";
import { packages } from "./createTypesPackages.mjs";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Expand All @@ -26,6 +27,9 @@ const verify = () => {
);
};

const gitShowFile = (commitish, path) =>
execSync(`git show "${commitish}":${path}`, { encoding: "utf-8" });

const go = async () => {
verify();

Expand All @@ -40,28 +44,51 @@ const go = async () => {
const newTSConfig = fs.readFileSync(localPackageJSONPath, "utf-8");
const pkgJSON = JSON.parse(newTSConfig);

// We'll need to map back from the filename in the npm package to the
// generated file in baselines inside the git tag
const thisPackageMeta = packages.find((p) => p.name === pkgJSON.name);

const dtsFiles = fs
.readdirSync(join(generatedDir, dirName))
.filter((f) => f.endsWith(".d.ts"));

/** @type {string[]} */
let releaseNotes = [];

// Look through each .d.ts file included in a package to
// determine if anything has changed
let upload = false;
for (const file of dtsFiles) {
const originalFilename = basename(
thisPackageMeta.files.find((f) => f.to === file).from
);

const generatedDTSPath = join(generatedDir, dirName, file);
const generatedDTSContent = fs.readFileSync(generatedDTSPath, "utf8");
const unpkgURL = `https://unpkg.com/${pkgJSON.name}/${file}`;

// This assumes we'll only _ever_ ship patches, which may change in the
// future someday.
const [maj, min, patch] = pkgJSON.version.split(".");
const olderVersion = `${maj}.${min}.${patch - 1}`;

try {
const npmDTSReq = await fetch(unpkgURL);
const npmDTSText = await npmDTSReq.text();
console.log(`Comparing ${file} from unpkg, to generated version:`);
printDiff(npmDTSText, generatedDTSContent);
const oldFile = gitShowFile(
`${pkgJSON.name}@${olderVersion}`,
`baselines/${originalFilename}`
);
console.log(`Comparing ${file} from ${olderVersion}, to now:`);
printDiff(oldFile, generatedDTSContent);

const title = `\n## \`${file}\`\n`;
const notes = generateChangelogFrom(oldFile, generatedDTSContent);
releaseNotes.push(title);
releaseNotes.push(notes.trim() === "" ? "No changes" : notes);

upload = upload || npmDTSText !== generatedDTSContent;
upload = upload || oldFile !== generatedDTSContent;
} catch (error) {
// Could not find a previous build
console.log(`
Could not get the file ${file} inside the npm package ${pkgJSON.name} from unpkg at ${unpkgURL}
Could not get the file ${file} inside the npm package ${pkgJSON.name} from tag ${olderVersion}.
Assuming that this means we need to upload this package.`);
upload = true;
}
Expand Down Expand Up @@ -93,8 +120,10 @@ Assuming that this means we need to upload this package.`);

uploaded.push(dirName);
}
}

console.log("\n# Release notes:");
console.log(releaseNotes.join("\n"), "\n\n");
}
// Warn if we did a dry run.
if (!process.env.NODE_AUTH_TOKEN) {
console.log(
Expand All @@ -109,7 +138,7 @@ Assuming that this means we need to upload this package.`);
}
};

async function createRelease(tag) {
async function createRelease(tag, body) {
const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN;
const octokit = new Octokit({ auth: authToken });

Expand All @@ -119,6 +148,8 @@ async function createRelease(tag) {
repo: "TypeScript-DOM-lib-generator",
tag_name: tag,
target_commitish: process.env.GITHUB_SHA,
name: tag,
body,
});
} catch (error) {
console.error(
Expand Down
1 change: 1 addition & 0 deletions src/migrate.ts → deploy/migrate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ if (!tscWD)

const generatedFiles = readdirSync("generated");
generatedFiles.forEach((file) => {
if (file == ".DS_Store") return;
const contents = readFileSync(join("generated", file), "utf8");
const newFilePath = join(tscWD, "src", "lib", file);
writeFileSync(newFilePath, contents);
Expand Down
39 changes: 39 additions & 0 deletions deploy/versionChangelog.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @ts-check

// npm run ts-changelog @types/web 0.0.1 0.0.3

import { generateChangelogFrom } from "../lib/changelog.js";
import { packages } from "./createTypesPackages.mjs";
import { execSync } from "child_process";
import { basename } from "path";

const [name, before, to] = process.argv.slice(2);
if (!name || !before || !to) {
throw new Error(
"Expected three arguments: package name, version before, version to"
);
}

const go = () => {
// We'll need to map back from the filename in the npm package to the
// generated file in baselines inside the git tag
const thisPackageMeta = packages.find((p) => p.name === name);
if (!thisPackageMeta)
throw new Error(`Could not find ${name} in ${packages.map((p) => p.name)}`);

for (const file of thisPackageMeta.files) {
const filename = `baselines/${basename(file.from)}`;
const beforeFileText = gitShowFile(`${name}@${before}`, filename);
const toFileText = gitShowFile(`${name}@${to}`, filename);

const notes = generateChangelogFrom(beforeFileText, toFileText);

console.log(`\n## \`${file.to}\`\n`);
console.log(notes.trim() === "" ? "No changes" : notes);
}
};

const gitShowFile = (commitish, path) =>
execSync(`git show "${commitish}":${path}`, { encoding: "utf-8" });

go();
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"lint": "eslint --max-warnings 0 src",
"test": "npm run lint && npm run build && node ./lib/test.js && node ./unittests/index.js",
"changelog": "tsc && node ./lib/changelog.js",
"migrate": "node ./lib/migrate.js",
"ts-changelog": "node ./deploy/versionChangelog.mjs",
"migrate": "node ./deploy/migrate.mjs",
"version": "npm i && tsc && node ./lib/version.js"
},
"author": {
Expand Down
21 changes: 14 additions & 7 deletions src/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,21 @@ function writeAddedRemovedInline(added: Set<string>, removed: Set<string>) {

const dom = "baselines/dom.generated.d.ts";

export function generate(): string {
export function generateDefaultFromRecentTag(): string {
const [base = gitLatestTag(), head = "HEAD"] = process.argv.slice(2);
const previous = gitShowFile(base, dom);
const current = gitShowFile(head, dom);
const changelog = generateChangelogFrom(previous, current);
if (!changelog.length) {
throw new Error(`No change reported between ${base} and ${head}.`);
}
return changelog;
}

export function generateChangelogFrom(
previous: string,
current: string
): string {
const {
interfaces: { added, removed, modified },
others,
Expand All @@ -150,6 +161,7 @@ export function generate(): string {
if (added.size || removed.size) {
outputs.push(writeAddedRemoved(added, removed));
}

if (modified.size) {
const modifiedOutput = [`## Modified\n`];
for (const [key, value] of modified.entries()) {
Expand All @@ -169,14 +181,9 @@ export function generate(): string {
}

const output = outputs.join("\n\n");

if (!output.length) {
throw new Error(`No change reported between ${base} and ${head}.`);
}

return output;
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
console.log(generate());
console.log(generateDefaultFromRecentTag());
}
4 changes: 2 additions & 2 deletions src/version.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { execSync } from "child_process";
import { readFile, writeFile } from "fs/promises";
import { generate } from "./changelog.js";
import { generateDefaultFromRecentTag } from "./changelog.js";

const output = generate();
const output = generateDefaultFromRecentTag();

const path = new URL("../CHANGELOG.md", import.meta.url);

Expand Down