Skip to content
Open
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
10 changes: 10 additions & 0 deletions .changeset/gamut-codemods-video-markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@skillsoft/gamut-codemods': minor
---

`scope-swap` now covers the Video and Markdown changes.

- `moved-exports` moves `Video` and `VideoProps` imports off the root onto `@skillsoft/gamut/Video`, splitting mixed imports and merging into an existing `/Video` import. Namespace imports, `export *`, and mocks that stub `Video` get a warning instead.
- `markdown-video-overrides` adds `iframeOverride` / `videoOverride` (with `Iframe` and `MarkdownVideo` imported from `/Video`) to each `<Markdown>` that didn't opt out, since `<Markdown>` no longer renders `<iframe>` and `<video>` through `Video` by default. It replaces the removed `skipDefaultOverrides.iframe` / `.video`, writing `iframeOverride={undefined}` where a skip was `true`, so a second run stays a no-op.
- `tsconfig-dom` (report only) warns about tsconfig files whose `lib` has no `"dom"`, since Video used to supply the DOM types through the root import.
- When a run moves Video, the next steps list the `@vidstack/react` and `react-player` peer dependencies.
9 changes: 9 additions & 0 deletions .changeset/gamut-video-markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@skillsoft/gamut': patch
---

Video and Markdown fixes.

- `@skillsoft/gamut/Video` now resolves under TypeScript's `moduleResolution: "node"` (node10) and in bundlers that ignore `package.json#exports`, through a `Video/package.json` stub.
- A Markdown `<video>` without `width` or `height` no longer passes `NaN` sizes to `Video`.
- A `<video>...</video>` written on one line is no longer wrapped in a `<p>`.
17 changes: 10 additions & 7 deletions packages/gamut-codemods/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,16 @@ Every skillsoft-gamut pull request publishes installable previews through pkg.pr

### What scope-swap changes

| Migration | What it does |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mf-shared` | Replaces `@codecademy/gamut-kit` in Module Federation `shared` config with the packages it bundled. Gamut becomes a real singleton, so test the host and remotes together. |
| `scope-rename` | Every `@codecademy/gamut*` module string becomes `@skillsoft/gamut*`: imports, exports, `require`, `import()`, `jest.mock`/`vi.mock`, `declare module`, and `import('x').T`. Also exact package-name strings elsewhere, like `transpilePackages`, with one warning per file, and package names in comments (`/dist` paths and removed packages are left for the leftovers). |
| `package-json` | Renames dependencies and sets their versions. A new package that's already listed keeps its value (a preview URL, say), and a `*` peer range stays `*`. Replaces `@codecademy/gamut-kit` with the individual packages. |
| `mdx-imports` | Runs the migrations above over `import`/`export` statements in `.mdx` files. Fenced code blocks are left alone. |
| `yarnrc` | `.yarnrc.yml` list items, like `npmPreapprovedPackages`: renames old package names, or drops them if a glob like `'@skillsoft/*'` already covers the new one. |
| Migration | What it does |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `moved-exports` | Moves `Video` and `VideoProps` off the root import onto `/Video`. Warns on namespace imports, `export *`, and mocks that stub `Video`. |
| `markdown-video-overrides` | Markdown no longer renders `<iframe>` and `<video>` through `Video` by default. Adds `iframeOverride` / `videoOverride` to each `<Markdown>` that didn't skip them, importing `Iframe` and `MarkdownVideo` from `/Video`. Drops the removed `skipDefaultOverrides.iframe` / `.video` keys, writing `iframeOverride={undefined}` where they were `true`. Warns on a `skipDefaultOverrides` it can't read. Skipped when every `@codecademy/gamut` range in the repo is already 74.0.0 or newer, since that release made the change upstream. |
| `mf-shared` | Replaces `@codecademy/gamut-kit` in Module Federation `shared` config with the packages it bundled. Gamut becomes a real singleton, so test the host and remotes together. |
| `scope-rename` | Every `@codecademy/gamut*` module string becomes `@skillsoft/gamut*`: imports, exports, `require`, `import()`, `jest.mock`/`vi.mock`, `declare module`, and `import('x').T`. Also exact package-name strings elsewhere, like `transpilePackages`, with one warning per file, and package names in comments (`/dist` paths and removed packages are left for the leftovers). |
| `package-json` | Renames dependencies and sets their versions. A new package that's already listed keeps its value (a preview URL, say), and a `*` peer range stays `*`. Replaces `@codecademy/gamut-kit` with the individual packages. |
| `mdx-imports` | Runs the migrations above over `import`/`export` statements in `.mdx` files. Fenced code blocks are left alone. |
| `yarnrc` | `.yarnrc.yml` list items, like `npmPreapprovedPackages`: renames old package names, or drops them if a glob like `'@skillsoft/*'` already covers the new one. |
| `tsconfig-dom` | Report only. Warns about `tsconfig` files whose `lib` has no `"dom"`, since Video used to supply the DOM types through the root import. |

### Known limits

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Box, Markdown, Video } from '@codecademy/gamut';

export const Post = ({ text }: { text: string }) => <Box><Markdown text={text} /><Video videoUrl="x" /></Box>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Box, Markdown } from '@skillsoft/gamut';
import { Video, Iframe, MarkdownVideo } from '@skillsoft/gamut/Video';

export const Post = ({ text }: { text: string }) => <Box><Markdown
text={text}
iframeOverride={{
component: Iframe
}}
videoOverride={{
component: MarkdownVideo
}} /><Video videoUrl="x" /></Box>;
25 changes: 25 additions & 0 deletions packages/gamut-codemods/src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,31 @@ describe('main: full run', () => {
expect(out).toMatch(/Reinstall \(yarn\)/);
});

it('moves Video, adds Markdown overrides, and flags tsconfig lib', async () => {
const dir = project({
'src/Post.tsx': POST,
'src/index.ts': "export * from '@codecademy/gamut';\n",
'docs/reexport.mdx': "export * from '@codecademy/gamut';\n",
'tsconfig.json': JSON.stringify({ compilerOptions: { lib: ['es2020'] } }),
});

expect(await main(['scope-swap', dir, '--force'])).toBe(0);

const post = read(dir, 'src/Post.tsx');
expect(post).toContain("from '@skillsoft/gamut/Video'");
expect(post).toContain('iframeOverride');

const out = output();
expect(out).toMatch(/Video needs two peer dependencies/);
expect(out).toMatch(/Some tsconfig files set "lib"/);
expect(out).toMatch(
/\[moved-exports\] .*src\/index\.ts:1 .*no longer re-exports Video/
);
expect(out).toMatch(
/\[moved-exports\] .*reexport\.mdx .*no longer re-exports Video/
);
});

it('writes nothing under --dry and skips the checklist and leftovers', async () => {
const pkg = JSON.stringify({
dependencies: { '@codecademy/gamut': '^73.0.0' },
Expand Down
39 changes: 39 additions & 0 deletions packages/gamut-codemods/src/__tests__/lib.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { dirtyTreeReason } from '../lib/git';
import { findLeftovers } from '../lib/leftovers';
import { isAtLeast, isNewerThan, minVersion } from '../lib/manifest';
import type { Migration } from '../lib/types';
import { markdownVideoOverrides } from '../migrations/markdown-video-overrides';
import { validatePreset } from '../presets';
import { scopeSwap } from '../presets/scope-swap';
import { manifest } from '../presets/scope-swap/manifest';
Expand Down Expand Up @@ -219,6 +220,44 @@ describe('isAtLeast', () => {
});
});

describe('markdown-video-overrides skipReason', () => {
const repo = (deps: Record<string, string>[]) => {
const dir = tmp();
deps.forEach((dependencies, i) => {
const pkgDir = i === 0 ? dir : path.join(dir, 'libs', String(i));
fs.mkdirSync(pkgDir, { recursive: true });
fs.writeFileSync(
path.join(pkgDir, 'package.json'),
JSON.stringify({ dependencies })
);
});
return dir;
};

it('skips when every pinned @codecademy/gamut is 74.0.0 or newer', () => {
const dir = repo([
{ '@codecademy/gamut': '74.0.0' },
{ '@codecademy/gamut': '*' },
]);
expect(markdownVideoOverrides.skipReason?.([dir], manifest)).toMatch(
/already at 74\.0\.0/
);
});

it('runs when any package is older than 74.0.0', () => {
const dir = repo([
{ '@codecademy/gamut': '74.0.0' },
{ '@codecademy/gamut': '^73.6.1' },
]);
expect(markdownVideoOverrides.skipReason?.([dir], manifest)).toBeNull();
});

it('runs when no version can be read', () => {
const dir = repo([{ '@codecademy/gamut': '*' }]);
expect(markdownVideoOverrides.skipReason?.([dir], manifest)).toBeNull();
});
});

describe('findLeftovers: scoped directives under a flat config', () => {
const directive =
'// eslint-disable-next-line @skillsoft/gamut/no-inline-style';
Expand Down
52 changes: 52 additions & 0 deletions packages/gamut-codemods/src/lib/package-ranges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import fs from 'node:fs';
import path from 'node:path';

import { walk } from './leftovers';

const FIELDS = [
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
];

const rangesIn = (file: string, pkg: string) => {
try {
const json = JSON.parse(fs.readFileSync(file, 'utf8')) as Record<
string,
Record<string, string> | undefined
>;
return FIELDS.flatMap((field) => json[field]?.[pkg] ?? []);
} catch {
return [];
}
};

/*
Every range `pkg` is listed at in the package.json files under the roots,
plus the nearest one above each root up to the git root (running on a
subfolder of a monorepo still depends on the root package.json).
*/
export const findPackageRanges = (roots: string[], pkg: string) => {
const files = new Set<string>();
for (const root of roots) {
const isDir = fs.statSync(root).isDirectory();
for (
let dir = isDir ? root : path.dirname(root);
dir !== path.dirname(dir);
dir = path.dirname(dir)
) {
const file = path.join(dir, 'package.json');
if (fs.existsSync(file)) {
files.add(file);
break;
}
if (fs.existsSync(path.join(dir, '.git'))) break;
}
if (!isDir) continue;
for (const file of walk(root)) {
if (path.basename(file) === 'package.json') files.add(file);
}
}
return [...files].flatMap((file) => rangesIn(file, pkg));
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Markdown } from '@codecademy/gamut';
import { Iframe } from './local/Iframe';

export const Post = ({ text }: { text: string }) => <Markdown text={text} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Markdown } from '@codecademy/gamut';
import { Iframe } from './local/Iframe';

export const Post = ({ text }: { text: string }) => <Markdown text={text} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
"a different import already uses the name Iframe or MarkdownVideo"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Markdown } from '@codecademy/gamut';

export const Post = ({ text }: { text: string }) => <Markdown text={text} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Markdown } from '@codecademy/gamut';
import { Iframe, MarkdownVideo } from '@codecademy/gamut/Video';

export const Post = ({ text }: { text: string }) => <Markdown
text={text}
iframeOverride={{
component: Iframe
}}
videoOverride={{
component: MarkdownVideo
}} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Markdown } from '@codecademy/gamut';
import { Video } from '@codecademy/gamut/Video';

export const Player = () => <Video videoUrl={url} />;
export const Post = ({ text }: { text: string }) => <Markdown text={text} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Markdown } from '@codecademy/gamut';
import { Video, Iframe, MarkdownVideo } from '@codecademy/gamut/Video';

export const Player = () => <Video videoUrl={url} />;
export const Post = ({ text }: { text: string }) => <Markdown
text={text}
iframeOverride={{
component: Iframe
}}
videoOverride={{
component: MarkdownVideo
}} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Markdown } from '@codecademy/gamut';

export const Off = <Markdown text={t} skipDefaultOverrides={{ iframe: true, video: true }} />;

export const Partial = <Markdown text={t} skipDefaultOverrides={{ a: true, iframe: false }} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Markdown } from '@codecademy/gamut';
import { Iframe, MarkdownVideo } from '@codecademy/gamut/Video';

export const Off = <Markdown text={t} iframeOverride={undefined} videoOverride={undefined} />;

export const Partial = <Markdown
text={t}
skipDefaultOverrides={{
a: true
}}
iframeOverride={{
component: Iframe
}}
videoOverride={{
component: MarkdownVideo
}} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Markdown } from '@codecademy/gamut';

export const Dynamic = <Markdown text={t} skipDefaultOverrides={options} />;

export const Flagged = <Markdown text={t} skipDefaultOverrides={{ iframe: flag }} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Markdown } from '@codecademy/gamut';
import { MarkdownVideo } from '@codecademy/gamut/Video';

export const Dynamic = <Markdown text={t} skipDefaultOverrides={options} />;

export const Flagged = <Markdown
text={t}
skipDefaultOverrides={{ iframe: flag }}
videoOverride={{
component: MarkdownVideo
}} />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
"skipDefaultOverrides is not an object literal",
"skipDefaultOverrides.iframe is not a boolean literal"
]
Loading