Skip to content
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: 35 additions & 2 deletions .github/workflows/pre-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,41 @@ jobs:
- name: Check dependencies alignment
run: yarn check-dependencies

- name: Run Workspace Lint
run: yarn lint
- name: Run packages lint
run: yarn lint:packages

- name: Run plugins lint
run: yarn lint:plugins

- name: Run remark-codeblock-language-as-title plugin checks
working-directory: plugins/remark-codeblock-language-as-title
run: yarn tsc

- name: Run remark-lint-no-dead-urls plugin checks
working-directory: plugins/remark-lint-no-dead-urls
run: yarn tsc && yarn test

- name: Run remark-snackplayer plugin checks
working-directory: plugins/remark-snackplayer
run: yarn tsc && yarn test
Comment on lines +39 to +49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: ideally we should have a script in the workspace package.json that takes care of tihs, rather than having to explicitely call yarn tsc and yarn test from the GH workflow

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good idea, will follow up on that!


lint-website:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Enable Corepack
run: corepack enable

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "22"
cache: yarn

- name: Install dependencies
run: yarn install --immutable

- name: Run Website Specific Lints
working-directory: website
Expand Down
3 changes: 2 additions & 1 deletion plugins/remark-codeblock-language-as-title/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"devDependencies": {
"@types/mdast": "^4.0.4",
"remark": "^15.0.1"
"remark": "^15.0.1",
"typescript": "^5.9.3"
}
}
14 changes: 12 additions & 2 deletions plugins/remark-codeblock-language-as-title/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
{
"extends": "@react-native/typescript-config/tsconfig.json",
"include": ["./src"],
"compilerOptions": {
"types": ["react", "node"],
"lib": ["ES2022", "DOM"],
"target": "es2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"allowSyntheticDefaultImports": true,
"allowImportingTsExtensions": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["./src"]
}
16 changes: 16 additions & 0 deletions plugins/remark-lint-no-dead-urls/jest.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = {
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
extensionsToTreatAsEsm: ['.ts'],
moduleFileExtensions: ['js', 'ts'],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.ts$',
transform: {
'^.+\\.ts$': [
'ts-jest',
{
useESM: true,
tsconfig: 'tsconfig.json',
},
],
},
};
9 changes: 6 additions & 3 deletions plugins/remark-lint-no-dead-urls/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@
"test": "yarn node --experimental-vm-modules $(yarn bin jest)"
},
"dependencies": {
"got": "^14.6.4",
"got": "^14.6.6",
"unified-lint-rule": "^3.0.1",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.3"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/mdast": "^4.0.4",
"dedent": "^1.7.0",
"dedent": "^1.7.1",
"jest": "^29.7.0",
"remark": "^15.0.1"
"remark": "^15.0.1",
"ts-jest": "^29.4.6",
"typescript": "^5.9.3"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,41 @@
* LICENSE file in the root directory of this source tree.
*/

import remark from 'remark';
import {remark} from 'remark';
import dedent from 'dedent';
import {jest} from '@jest/globals';
import {jest, describe, beforeEach, test, expect} from '@jest/globals';

jest.unstable_mockModule('../lib.js', () => ({
fetch: jest.fn(),
const mockFetch = jest.fn() as jest.MockedFunction<
(url: string, method: unknown, options?: object) => Promise<number>
>;

jest.unstable_mockModule('../lib.ts', () => ({
fetch: mockFetch,
}));

const {fetch} = await import('../lib.js');
const plugin = (await import('../')).default;
const plugin = (await import('../index.ts')).default;

const processMarkdown = (md, opts) => {
function processMarkdown(md: string, opts = {}) {
return remark().use(plugin, opts).process(md);
};
}

describe('remark-lint-no-dead-urls', () => {
beforeEach(() => fetch.mockReset());
beforeEach(() => mockFetch.mockReset());

test('works with no URLs', () => {
test('works with no URLs', async () => {
const lint = processMarkdown(dedent`
# Title

No URLs in here.
`);

return lint.then(vFile => {
expect(fetch).toHaveBeenCalledTimes(0);
expect(vFile.messages.length).toBe(0);
});
const vFile = await lint;
expect(mockFetch).toHaveBeenCalledTimes(0);
expect(vFile.messages.length).toBe(0);
});

test('works a good, bad a local link', () => {
fetch.mockReturnValueOnce(200).mockReturnValueOnce(404);
test('works a good, bad a local link', async () => {
mockFetch.mockResolvedValueOnce(200).mockResolvedValueOnce(404);

const lint = processMarkdown(
dedent`
Expand All @@ -51,17 +53,16 @@ describe('remark-lint-no-dead-urls', () => {
`
);

return lint.then(vFile => {
expect(fetch).toHaveBeenCalledTimes(2);
expect(vFile.messages.length).toBe(1);
expect(vFile.messages[0].reason).toBe(
'Link to https://github.com/unified/oops is broken'
);
});
const vFile = await lint;
expect(mockFetch).toHaveBeenCalledTimes(2);
expect(vFile.messages.length).toBe(1);
expect(vFile.messages[0].reason).toBe(
'Link to https://github.com/unified/oops is broken'
);
}, 15000);

test('works with definitions and images', () => {
fetch.mockReturnValueOnce(200).mockReturnValueOnce(404);
test('works with definitions and images', async () => {
mockFetch.mockResolvedValueOnce(200).mockResolvedValueOnce(404);

const lint = processMarkdown(
dedent`
Expand All @@ -80,27 +81,25 @@ describe('remark-lint-no-dead-urls', () => {
}
);

return lint.then(vFile => {
expect(fetch).toHaveBeenCalledTimes(2);
expect(vFile.messages.length).toBe(1);
expect(vFile.messages[0].reason).toBe('Link to /oops/broken is broken');
});
const vFile = await lint;
expect(mockFetch).toHaveBeenCalledTimes(2);
expect(vFile.messages.length).toBe(1);
expect(vFile.messages[0].reason).toBe('Link to /oops/broken is broken');
});

test('skips URLs with unsupported protocols', () => {
test('skips URLs with unsupported protocols', async () => {
const lint = processMarkdown(dedent`
[Send me an email.](mailto:me@me.com)
[Look at this file.](ftp://path/to/file.txt)
[Special schema.](flopper://a/b/c)
`);

return lint.then(vFile => {
expect(fetch).toHaveBeenCalledTimes(0);
expect(vFile.messages.length).toBe(0);
});
const vFile = await lint;
expect(mockFetch).toHaveBeenCalledTimes(0);
expect(vFile.messages.length).toBe(0);
});

test('localhost', () => {
test('localhost', async () => {
const lint = processMarkdown(
dedent`
- [http://localhost](http://localhost)
Expand All @@ -114,12 +113,11 @@ describe('remark-lint-no-dead-urls', () => {
`
);

return lint.then(vFile => {
expect(vFile.messages.length).toBe(0);
});
const vFile = await lint;
expect(vFile.messages.length).toBe(0);
});

test('local IP 127.0.0.1', () => {
test('local IP 127.0.0.1', async () => {
const lint = processMarkdown(
dedent`
- [http://127.0.0.1](http://127.0.0.1)
Expand All @@ -133,23 +131,21 @@ describe('remark-lint-no-dead-urls', () => {
`
);

return lint.then(vFile => {
expect(vFile.messages.length).toBe(0);
});
const vFile = await lint;
expect(vFile.messages.length).toBe(0);
});

test.each([
'[Ignore this](http://www.url-to-ignore.com)',
'[Ignore this](http://www.url-to-ignore.com/somePath)',
'[Ignore this](http://www.url-to-ignore.com/somePath?withQuery=wow)',
'[its complicated](http://url-to-ignore.com/somePath/maybe)',
])('skipUrlPatterns for content: %s', markdownContent => {
])('skipUrlPatterns for content: %s', async markdownContent => {
const lint = processMarkdown(markdownContent, {
skipUrlPatterns: [/^http:\/\/(.*)url-to-ignore\.com/],
});

return lint.then(vFile => {
expect(vFile.messages.length).toBe(0);
});
const vFile = await lint;
expect(vFile.messages.length).toBe(0);
});
});
2 changes: 1 addition & 1 deletion plugins/remark-lint-no-dead-urls/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {lintRule} from 'unified-lint-rule';
import {visit} from 'unist-util-visit';
import type {VFile} from 'vfile';

import {fetch} from './lib.js';
import {fetch} from './lib.ts';

const linkCache = new Map();

Expand Down
14 changes: 12 additions & 2 deletions plugins/remark-lint-no-dead-urls/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
{
"extends": "@react-native/typescript-config/tsconfig.json",
"include": ["./src"],
"compilerOptions": {
"types": ["react", "node"],
"lib": ["ES2022", "DOM"],
"target": "es2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"allowSyntheticDefaultImports": true,
"allowImportingTsExtensions": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["./src"]
}
7 changes: 3 additions & 4 deletions plugins/remark-snackplayer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,15 @@
"test": "yarn tape tests/index.ts"
},
"dependencies": {
"dedent": "^1.7.0",
"object.fromentries": "^2.0.8",
"dedent": "^1.7.1",
"unist-util-visit-parents": "^3.1.1"
},
"devDependencies": {
"@types/mdast": "^4.0.4",
"@types/object.fromentries": "^2.0.4",
"@types/tape": "^5.8.1",
"remark": "^15.0.1",
"remark-mdx": "^3.1.1",
"tape": "^5.9.0"
"tape": "^5.9.0",
"typescript": "^5.9.3"
}
}
5 changes: 2 additions & 3 deletions plugins/remark-snackplayer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
'use strict';

import visit from 'unist-util-visit-parents';
import fromEntries from 'object.fromentries';
import type {Code} from 'mdast';
import type {Node} from 'unist';

function parseParams(paramString = '') {
const params = fromEntries(new URLSearchParams(paramString));
const params = Object.fromEntries(new URLSearchParams(paramString).entries());

if (!params.platform) {
params.platform = 'web';
Expand Down Expand Up @@ -90,7 +89,7 @@ export default function SnackPlayer() {
const nodesToProcess: Promise<void>[] = [];
visit(tree, 'code', (node: Node) => {
if ('lang' in node && node.lang === 'SnackPlayer') {
nodesToProcess.push(toJsxNode(node as Code));
nodesToProcess.push(toJsxNode(node as unknown as Code));
}
});
await Promise.all(nodesToProcess);
Expand Down
18 changes: 15 additions & 3 deletions plugins/remark-snackplayer/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,36 @@ import {remark} from 'remark';
import remarkMdx from 'remark-mdx';
import test from 'tape';

import SnackPlayer from '../src/index';
import SnackPlayer from '../src/index.ts';

function read(name: string) {
return fs.readFileSync(path.join(import.meta.dirname, name), 'utf8');
}

function cleanupStringContent(content: string) {
return content.replaceAll('\r\n', '\n').trim().replace(/\s+/g, '');
}

test('remark-snackplayer', async t => {
const processor = remark().use(remarkMdx).use(SnackPlayer);

const in1 = read('markdown/test1.md');
const out1 = read('output/output1.html');
const file1 = await processor.process(in1);
t.equal(String(file1), out1, 'With 1 Code Block');
t.equal(
cleanupStringContent(String(file1)),
cleanupStringContent(out1),
'With 1 Code Block'
);

const in2 = read('markdown/test2.md');
const out2 = read('output/output2.html');
const file2 = await processor.process(in2);
t.equal(String(file2), out2, 'With 2 Code Blocks');
t.equal(
cleanupStringContent(String(file2)),
cleanupStringContent(out2),
'With 2 Code Blocks'
);

t.end();
});
1 change: 1 addition & 0 deletions plugins/remark-snackplayer/tests/output/output1.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
data-snack-theme="light"
data-snack-preview="true"
data-snack-loading="lazy"
data-snack-device-appearance="light"
data-snack-device-frame="false"
/>
Loading