Skip to content
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

⬆️ Add simple upgrade/downgrade package for MyST AST #1802

Merged
merged 24 commits into from
Feb 8, 2025
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
9 changes: 9 additions & 0 deletions .changeset/eight-tools-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"myst-transforms": patch
"myst-spec-ext": patch
"myst-to-docx": patch
"myst-config": patch
"myst-cli": patch
---

Change footnotes to use enumerator over number
5 changes: 5 additions & 0 deletions .changeset/eleven-keys-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"myst-config": patch
---

Add version to config file
5 changes: 5 additions & 0 deletions .changeset/red-suits-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"myst-cli": patch
---

Add version to site content outputs
5 changes: 5 additions & 0 deletions .changeset/sour-spiders-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-migrate': patch
---

Add myst-migrate package
1 change: 1 addition & 0 deletions docs/myst.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ project:
- file: document-parts.md
- file: settings.md
- file: glossary.md
- file: versions.md
- title: Contribute
children:
- file: contributing.md
Expand Down
18 changes: 18 additions & 0 deletions docs/versions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: MyST Content Versions
---

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.

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.

# MyST Versions

## Version 1 - 2025-02-07 - Footnote Numbering

The footnotes have dropped backwards compatibility with `number`, instead using `enumerator` on both the `FootnoteReference` and `FootnoteDefinition` nodes.
Previous versions of the AST had both of these defined. The `enumerator` property is used in all other numberings of figures, sections, equations, etc.

## Version 0 - Pre 2025-02-01

This is the first version of MyST AST considered to be versioned, subsequent releases will have changes for migrating the content between versions.
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion packages/myst-cli/src/build/cff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import type { ExportWithOutput, ExportFnOptions } from './types.js';
import { cleanOutput } from './utils/cleanOutput.js';
import { getFileContent } from './utils/getFileContent.js';
import { resolveFrontmatterParts } from '../utils/resolveFrontmatterParts.js';
import { parseMyst } from '../process/myst.js';

function exportOptionsToCFF(exportOptions: ExportWithOutput): CFF {
// Handle overlap of key "format" between CFF and export
Expand Down
4 changes: 3 additions & 1 deletion packages/myst-cli/src/build/site/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { collectExportOptions } from '../utils/collectExportOptions.js';
import { filterPages } from '../../project/load.js';
import { getRawFrontmatterFromFile } from '../../process/file.js';
import { castSession } from '../../session/cache.js';
import { SPEC_VERSION } from '../../spec-version.js';

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

Expand Down Expand Up @@ -417,9 +418,10 @@ export async function getSiteManifest(
validatedFrontmatter.options = resolvedOptions;
const parts = resolveFrontmatterParts(session, validatedFrontmatter);
const manifest: SiteManifest = {
version: SPEC_VERSION,
myst: version,
...validatedFrontmatter,
parts,
myst: version,
nav: nav || [],
actions: actions || [],
projects: siteProjects,
Expand Down
1 change: 1 addition & 0 deletions packages/myst-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export * from './session/index.js';
export * from './store/index.js';
export * from './transforms/index.js';
export * from './utils/index.js';
export * from './spec-version.js';
export { default as version } from './version.js';
2 changes: 2 additions & 0 deletions packages/myst-cli/src/process/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { loadReferences } from './loadReferences.js';
import type { TransformFn } from './mdast.js';
import { finalizeMdast, postProcessMdast, transformMdast } from './mdast.js';
import { toSectionedParts, buildHierarchy, sectionToHeadingLevel } from './search.js';
import { SPEC_VERSION } from '../spec-version.js';

const WEB_IMAGE_EXTENSIONS = [
ImageExtensions.mp4,
Expand Down Expand Up @@ -412,6 +413,7 @@ export async function writeFile(
const parts = resolveFrontmatterParts(session, frontmatter);
const frontmatterWithExports = { ...frontmatter, exports, downloads, parts };
const mystData: MystData = {
version: SPEC_VERSION,
kind,
sha256,
slug,
Expand Down
1 change: 1 addition & 0 deletions packages/myst-cli/src/spec-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SPEC_VERSION = 1;
1 change: 1 addition & 0 deletions packages/myst-cli/src/transforms/crossReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function mystDataFilename(dataUrl: string) {
}

export type MystData = {
version: number;
kind?: SourceFileKind;
sha256?: string;
slug?: string;
Expand Down
1 change: 1 addition & 0 deletions packages/myst-config/src/site/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type ManifestProject = {
} & Omit<ProjectFrontmatter, 'downloads' | 'exports' | 'parts'>;

export type SiteManifest = Omit<SiteFrontmatter, 'parts'> & {
version: number;
myst: string;
id?: string;
projects?: ManifestProject[];
Expand Down
4 changes: 4 additions & 0 deletions packages/myst-migrate/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ['curvenote'],
};
3 changes: 3 additions & 0 deletions packages/myst-migrate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# myst-migrate

Utilities for upgrading and downgrading MyST ASTs
30 changes: 30 additions & 0 deletions packages/myst-migrate/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "myst-migrate",
"sideEffects": false,
"version": "0.0.0",
"type": "module",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"license": "MIT",
"scripts": {
"clean": "rimraf dist",
"lint": "eslint \"src/**/!(*.spec).ts\" -c ./.eslintrc.cjs",
"lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\"",
"test": "vitest run",
"test:watch": "vitest watch",
"build:esm": "tsc",
"build": "npm-run-all -l clean -p build:esm"
},
"dependencies": {
"unist-util-select": "^4.0.3",
"unist-util-visit": "^4.1.2",
"vfile": "^5.0.0",
"vfile-message": "^3.0.0"
},
"devDependencies": {
"@jupyterlab/nbformat": "^3.5.2"
}
}
50 changes: 50 additions & 0 deletions packages/myst-migrate/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { IFile, Options } from './types.js';
import { MIGRATIONS } from './migrations.js';

export { MIGRATIONS } from './migrations.js';

/**
* Migrate function:
* @param src - The page to be migrated
* @param options - to: desired target version, log: Logger
*/
export async function migrate(src: IFile, opts?: Options): Promise<IFile> {
const to = opts?.to ?? MIGRATIONS.length;
let currentVersion = src.version || 0;

// If the current version is already at the target, do nothing
if (currentVersion === to) {
opts?.log?.debug(`Already at version ${to}. No migration needed.`);
return src;
}

// If currentVersion < toVersion, apply upgrades forward
while (currentVersion < to) {
if (currentVersion >= MIGRATIONS.length) {
throw new Error(
`No migration available to go from version ${currentVersion} to ${currentVersion + 1}`,
);
}
const migration = MIGRATIONS[currentVersion];
opts?.log?.debug(`Upgrading from v${currentVersion} to v${currentVersion + 1}...`);
await migration.upgrade(src);
currentVersion++;
src.version = currentVersion;
}

// If currentVersion > toVersion, apply downgrades backward
while (currentVersion > to) {
if (currentVersion - 1 >= MIGRATIONS.length) {
throw new Error(
`No migration available to go from version ${currentVersion} down to ${currentVersion - 1}`,
);
}
const migration = MIGRATIONS[currentVersion - 1];
opts?.log?.debug(`Downgrading from v${currentVersion} to v${currentVersion - 1}...`);
await migration.downgrade(src);
currentVersion--;
src.version = currentVersion;
}

return src;
}
4 changes: 4 additions & 0 deletions packages/myst-migrate/src/migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { Migration } from './types.js';
import * as v1 from './v1_footnotes.js';

export const MIGRATIONS: Migration[] = [v1];
Copy link
Member

Choose a reason for hiding this comment

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

This types each module for an upgrade/downgrade/description, and we then just put it in an array.

Loading
Loading