Skip to content

Commit 75bbb18

Browse files
agoose77rowanc1
andauthored
⬆️ Add simple upgrade/downgrade package for MyST AST (#1802)
Co-authored-by: Rowan Cockett <[email protected]>
1 parent 7448083 commit 75bbb18

File tree

28 files changed

+719
-20
lines changed

28 files changed

+719
-20
lines changed

.changeset/eight-tools-live.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"myst-transforms": patch
3+
"myst-spec-ext": patch
4+
"myst-to-docx": patch
5+
"myst-config": patch
6+
"myst-cli": patch
7+
---
8+
9+
Change footnotes to use enumerator over number

.changeset/eleven-keys-explain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"myst-config": patch
3+
---
4+
5+
Add version to config file

.changeset/red-suits-hug.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"myst-cli": patch
3+
---
4+
5+
Add version to site content outputs

.changeset/sour-spiders-push.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'myst-migrate': patch
3+
---
4+
5+
Add myst-migrate package

docs/myst.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ project:
153153
- file: document-parts.md
154154
- file: settings.md
155155
- file: glossary.md
156+
- file: versions.md
156157
- title: Contribute
157158
children:
158159
- file: contributing.md

docs/versions.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
title: MyST Content Versions
3+
---
4+
5+
The following page describes changes to the MyST content that is served from the `.json` endpoint of a MyST Site. The `myst-compat` package can be used to translate between versions by upgrading and downgrading between versions.
6+
7+
The version is a string integer (i.e. `'1'` or `'2'`) and is incremented with every change to the content of a MyST page, which includes metadata as well as the MyST AST.
8+
9+
# MyST Versions
10+
11+
## Version 1 - 2025-02-07 - Footnote Numbering
12+
13+
The footnotes have dropped backwards compatibility with `number`, instead using `enumerator` on both the `FootnoteReference` and `FootnoteDefinition` nodes.
14+
Previous versions of the AST had both of these defined. The `enumerator` property is used in all other numberings of figures, sections, equations, etc.
15+
16+
## Version 0 - Pre 2025-02-01
17+
18+
This is the first version of MyST AST considered to be versioned, subsequent releases will have changes for migrating the content between versions.

package-lock.json

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

packages/myst-cli/src/build/cff.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import type { ExportWithOutput, ExportFnOptions } from './types.js';
1515
import { cleanOutput } from './utils/cleanOutput.js';
1616
import { getFileContent } from './utils/getFileContent.js';
1717
import { resolveFrontmatterParts } from '../utils/resolveFrontmatterParts.js';
18-
import { parseMyst } from '../process/myst.js';
1918

2019
function exportOptionsToCFF(exportOptions: ExportWithOutput): CFF {
2120
// Handle overlap of key "format" between CFF and export

packages/myst-cli/src/build/site/manifest.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { collectExportOptions } from '../utils/collectExportOptions.js';
2626
import { filterPages } from '../../project/load.js';
2727
import { getRawFrontmatterFromFile } from '../../process/file.js';
2828
import { castSession } from '../../session/cache.js';
29+
import { SPEC_VERSION } from '../../spec-version.js';
2930

3031
type ManifestProject = Required<SiteManifest>['projects'][0];
3132

@@ -417,9 +418,10 @@ export async function getSiteManifest(
417418
validatedFrontmatter.options = resolvedOptions;
418419
const parts = resolveFrontmatterParts(session, validatedFrontmatter);
419420
const manifest: SiteManifest = {
421+
version: SPEC_VERSION,
422+
myst: version,
420423
...validatedFrontmatter,
421424
parts,
422-
myst: version,
423425
nav: nav || [],
424426
actions: actions || [],
425427
projects: siteProjects,

packages/myst-cli/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export * from './session/index.js';
1010
export * from './store/index.js';
1111
export * from './transforms/index.js';
1212
export * from './utils/index.js';
13+
export * from './spec-version.js';
1314
export { default as version } from './version.js';

packages/myst-cli/src/process/site.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { loadReferences } from './loadReferences.js';
4545
import type { TransformFn } from './mdast.js';
4646
import { finalizeMdast, postProcessMdast, transformMdast } from './mdast.js';
4747
import { toSectionedParts, buildHierarchy, sectionToHeadingLevel } from './search.js';
48+
import { SPEC_VERSION } from '../spec-version.js';
4849

4950
const WEB_IMAGE_EXTENSIONS = [
5051
ImageExtensions.mp4,
@@ -412,6 +413,7 @@ export async function writeFile(
412413
const parts = resolveFrontmatterParts(session, frontmatter);
413414
const frontmatterWithExports = { ...frontmatter, exports, downloads, parts };
414415
const mystData: MystData = {
416+
version: SPEC_VERSION,
415417
kind,
416418
sha256,
417419
slug,

packages/myst-cli/src/spec-version.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const SPEC_VERSION = 1;

packages/myst-cli/src/transforms/crossReferences.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ function mystDataFilename(dataUrl: string) {
1717
}
1818

1919
export type MystData = {
20+
version: number;
2021
kind?: SourceFileKind;
2122
sha256?: string;
2223
slug?: string;

packages/myst-config/src/site/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ type ManifestProject = {
7171
} & Omit<ProjectFrontmatter, 'downloads' | 'exports' | 'parts'>;
7272

7373
export type SiteManifest = Omit<SiteFrontmatter, 'parts'> & {
74+
version: number;
7475
myst: string;
7576
id?: string;
7677
projects?: ManifestProject[];

packages/myst-migrate/.eslintrc.cjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
root: true,
3+
extends: ['curvenote'],
4+
};

packages/myst-migrate/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# myst-migrate
2+
3+
Utilities for upgrading and downgrading MyST ASTs

packages/myst-migrate/package.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "myst-migrate",
3+
"sideEffects": false,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"exports": "./dist/index.js",
7+
"types": "./dist/index.d.ts",
8+
"files": [
9+
"dist"
10+
],
11+
"license": "MIT",
12+
"scripts": {
13+
"clean": "rimraf dist",
14+
"lint": "eslint \"src/**/!(*.spec).ts\" -c ./.eslintrc.cjs",
15+
"lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\"",
16+
"test": "vitest run",
17+
"test:watch": "vitest watch",
18+
"build:esm": "tsc",
19+
"build": "npm-run-all -l clean -p build:esm"
20+
},
21+
"dependencies": {
22+
"unist-util-select": "^4.0.3",
23+
"unist-util-visit": "^4.1.2",
24+
"vfile": "^5.0.0",
25+
"vfile-message": "^3.0.0"
26+
},
27+
"devDependencies": {
28+
"@jupyterlab/nbformat": "^3.5.2"
29+
}
30+
}

packages/myst-migrate/src/index.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { IFile, Options } from './types.js';
2+
import { MIGRATIONS } from './migrations.js';
3+
4+
export { MIGRATIONS } from './migrations.js';
5+
6+
/**
7+
* Migrate function:
8+
* @param src - The page to be migrated
9+
* @param options - to: desired target version, log: Logger
10+
*/
11+
export async function migrate(src: IFile, opts?: Options): Promise<IFile> {
12+
const to = opts?.to ?? MIGRATIONS.length;
13+
let currentVersion = src.version || 0;
14+
15+
// If the current version is already at the target, do nothing
16+
if (currentVersion === to) {
17+
opts?.log?.debug(`Already at version ${to}. No migration needed.`);
18+
return src;
19+
}
20+
21+
// If currentVersion < toVersion, apply upgrades forward
22+
while (currentVersion < to) {
23+
if (currentVersion >= MIGRATIONS.length) {
24+
throw new Error(
25+
`No migration available to go from version ${currentVersion} to ${currentVersion + 1}`,
26+
);
27+
}
28+
const migration = MIGRATIONS[currentVersion];
29+
opts?.log?.debug(`Upgrading from v${currentVersion} to v${currentVersion + 1}...`);
30+
await migration.upgrade(src);
31+
currentVersion++;
32+
src.version = currentVersion;
33+
}
34+
35+
// If currentVersion > toVersion, apply downgrades backward
36+
while (currentVersion > to) {
37+
if (currentVersion - 1 >= MIGRATIONS.length) {
38+
throw new Error(
39+
`No migration available to go from version ${currentVersion} down to ${currentVersion - 1}`,
40+
);
41+
}
42+
const migration = MIGRATIONS[currentVersion - 1];
43+
opts?.log?.debug(`Downgrading from v${currentVersion} to v${currentVersion - 1}...`);
44+
await migration.downgrade(src);
45+
currentVersion--;
46+
src.version = currentVersion;
47+
}
48+
49+
return src;
50+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { Migration } from './types.js';
2+
import * as v1 from './v1_footnotes.js';
3+
4+
export const MIGRATIONS: Migration[] = [v1];

0 commit comments

Comments
 (0)