Skip to content

Commit

Permalink
Convert source to TypeScript (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
delucis authored Jul 17, 2023
1 parent 6821423 commit 744ddc8
Show file tree
Hide file tree
Showing 21 changed files with 104 additions and 63 deletions.
10 changes: 10 additions & 0 deletions .changeset/gorgeous-lizards-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"astro-embed": minor
"@astro-community/astro-embed-integration": minor
"@astro-community/astro-embed-twitter": minor
"@astro-community/astro-embed-utils": minor
"@astro-community/astro-embed-vimeo": minor
"@astro-community/astro-embed-youtube": minor
---

Convert all files to TypeScript to provide full type information to users
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ If you think you’re ready to go, let’s get started.
"description": "Component to easily embed [SERVICE-NAME] on your Astro site",
"type": "module",
"exports": {
".": "./index.js",
"./matcher": "./matcher.js"
".": "./index.ts",
"./matcher": "./matcher.ts"
},
"repository": {
"type": "git",
Expand All @@ -52,13 +52,13 @@ If you think you’re ready to go, let’s get started.

Add an Astro component to your package and call it something understandable like `Tweet.astro` or `YouTube.astro`. This is the tricky bit — this part’s down to you! This is normal Astro component, so you can import styles, node modules etc. just like you would inside an Astro project.

One important thing: to support auto-embeds using the Astro integration, we’ll need access to a method that will extract an ID from a valid URL from the integration. The recommended way to do that is to create a `matcher.js` file and export a function that converts a URL to an ID. You can import and use that in your component, but having it in a separate file helps us hook up the integration functionality more easily.
One important thing: to support auto-embeds using the Astro integration, we’ll need access to a method that will extract an ID from a valid URL from the integration. The recommended way to do that is to create a `matcher.ts` file and export a function that converts a URL to an ID. You can import and use that in your component, but having it in a separate file helps us hook up the integration functionality more easily.

### Expose your component

In order for other projects to easily import your Astro component, we need to expose it to them.

To do this, create an `index.js` file. It usually will look something like this:
To do this, create an `index.ts` file. It usually will look something like this:

```js
export { default as Tweet } from './Tweet.astro';
Expand All @@ -84,7 +84,7 @@ Add a README file to your component package that shows how to use it. You can ch

### Add your plugin to the integration

Import your URL matcher function and add it and your component name to the `matchers` array in `astro-embed-integration/remark-plugin.js`.
Import your URL matcher function and add it and your component name to the `matchers` array in `astro-embed-integration/remark-plugin.ts`.

### Open a PR!

Expand Down
26 changes: 21 additions & 5 deletions package-lock.json

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

Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import createEmbedPlugin, { componentNames } from './remark-plugin.js';
import type { AstroIntegration } from 'astro';
import createEmbedPlugin, { componentNames } from './remark-plugin';
import AutoImport from 'astro-auto-import';
const importNamespace = 'AuToImPoRtEdAstroEmbed';

/**
* Astro embed MDX integration.
*/
export default function embed() {
/** @type {import('astro').AstroIntegration} */
const AstroEmbed = {
const AstroEmbed: AstroIntegration = {
name: 'astro-embed',
hooks: {
'astro:config:setup': ({ updateConfig }) => {
Expand Down
7 changes: 4 additions & 3 deletions packages/astro-embed-integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"description": "Astro integration to automatically convert URLs in Markdown files to embeds",
"type": "module",
"exports": {
".": "./index.js"
".": "./index.ts"
},
"files": [
"index.js",
"remark-plugin.js"
"index.ts",
"remark-plugin.ts"
],
"repository": {
"type": "git",
Expand All @@ -24,6 +24,7 @@
"@astro-community/astro-embed-twitter": "^0.4.0",
"@astro-community/astro-embed-vimeo": "^0.2.0",
"@astro-community/astro-embed-youtube": "^0.3.0",
"@types/unist": "^2.0.0",
"astro-auto-import": "^0.3.0",
"unist-util-select": "^4.0.1"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import { selectAll } from 'unist-util-select';
import { selectAll, Node } from 'unist-util-select';
import twitterMatcher from '@astro-community/astro-embed-twitter/matcher';
import vimeoMatcher from '@astro-community/astro-embed-vimeo/matcher';
import youtubeMatcher from '@astro-community/astro-embed-youtube/matcher';

/** @type {[(url: string) => string | undefined, string][]} */
const matchers = [
[twitterMatcher, 'Tweet'],
[vimeoMatcher, 'Vimeo'],
[youtubeMatcher, 'YouTube'],
];
] as const;
export const componentNames = matchers.map(([, name]) => name);

export default function createPlugin({ importNamespace }) {
export default function createPlugin({
importNamespace,
}: {
importNamespace: string;
}) {
/**
* Get the name of the embed component for this URL
* @param {string} url URL to test
* @returns Component node for this URL or undefined if none matched
*/
function getComponent(url) {
function getComponent(url: string) {
for (const [matcher, componentName] of matchers) {
const id = matcher(url);
if (id) {
Expand All @@ -30,11 +33,17 @@ export default function createPlugin({ importNamespace }) {
};
}
}
return undefined;
}

function transformer(tree) {
/** @type {(import('unist').Node & { url?: string; value?: string; children?: import('unist').Node[] })[]} */
const nodes = selectAll('paragraph > link:only-child', tree);
type Link = Node & {
url?: string;
value?: string;
children?: Node[];
};

function transformer(tree: Node) {
const nodes: Link[] = selectAll('paragraph > link:only-child', tree);

nodes.forEach((node) => {
const { url } = node;
Expand All @@ -43,6 +52,7 @@ export default function createPlugin({ importNamespace }) {

const component = getComponent(url);
if (component) {
// @ts-expect-error We’re overriding the initial node type with arbitrary data.
for (const key in component) node[key] = component[key];
}
});
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const urlPattern =

/**
* Return a Tweet URL from a URL if it matches the pattern.
* @param {string} url URL to test
* @returns {string|undefined} A Tweet ID or undefined if none matched
* @param url URL to test
* @returns A Tweet ID or undefined if none matched
*/
export default function urlMatcher(url) {
export default function urlMatcher(url: string): string | undefined {
const match = url.match(urlPattern);
return match?.[0];
}
8 changes: 4 additions & 4 deletions packages/astro-embed-twitter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"description": "Component to easily embed Tweets on your Astro site",
"type": "module",
"exports": {
".": "./index.js",
"./matcher": "./matcher.js"
".": "./index.ts",
"./matcher": "./matcher.ts"
},
"files": [
"index.js",
"matcher.js",
"index.ts",
"matcher.ts",
"Tweet.astro",
"Tweet.css"
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
class LRU extends Map {
constructor(maxSize) {
class LRU<K, V> extends Map<K, V> {
constructor(private readonly maxSize: number) {
super();
this.maxSize = maxSize;
}

get(key) {
override get(key: K): V | undefined {
const value = super.get(key);
if (value) this.#touch(key, value);
return value;
}

set(key, value) {
override set(key: K, value: V): this {
this.#touch(key, value);
if (this.size > this.maxSize) this.delete(this.keys().next().value);
return this;
}

#touch(key, value) {
#touch(key: K, value: V): void {
this.delete(key);
super.set(key, value);
}
}

const cache = new LRU(1000);
const cache = new LRU<string, Record<string, any>>(1000);

const formatError = (...lines) => lines.join('\n ');
const formatError = (...lines: string[]) => lines.join('\n ');

/**
* Fetch a URL and parse it as JSON, but catch errors to stop builds erroring.
* @param {string} url URL to fetch
* @param url URL to fetch
* @returns {Promise<Record<string, any> | undefined>}
*/
export async function safeGet(url) {
export async function safeGet(
url: string
): Promise<Record<string, any> | undefined> {
try {
const cached = cache.get(url);
if (cached) return cached;
// eslint-disable-next-line no-undef -- fetch is bootstrapped by Vite/Astro when not available
const res = await fetch(url);
if (!res.ok)
throw new Error(
Expand All @@ -47,7 +47,8 @@ export async function safeGet(url) {
const json = await res.json();
cache.set(url, json);
return json;
} catch (e) {
} catch (e: any) {
console.error(formatError(`[error] astro-embed`, e?.message ?? e));
return undefined;
}
}
4 changes: 2 additions & 2 deletions packages/astro-embed-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"description": "Shared utility methods for astro-embed components",
"type": "module",
"exports": {
".": "./index.js"
".": "./index.ts"
},
"files": [
"index.js"
"index.ts"
],
"repository": {
"type": "git",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const urlPattern =

/**
* Extract a Vimeo ID from a URL if it matches the pattern.
* @param {string} url URL to test
* @returns {string|undefined} A Vimeo video ID or undefined if none matched
* @param url URL to test
* @returns A Vimeo video ID or undefined if none matched
*/
export default function matcher(url) {
export default function matcher(url: string): string | undefined {
const match = url.match(urlPattern);
return match?.[3];
}
8 changes: 4 additions & 4 deletions packages/astro-embed-vimeo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"description": "Component to easily embed Vimeo videos on your Astro site",
"type": "module",
"exports": {
".": "./index.js",
"./matcher": "./matcher.js"
".": "./index.ts",
"./matcher": "./matcher.ts"
},
"files": [
"index.js",
"matcher.js",
"index.ts",
"matcher.ts",
"Vimeo.astro",
"Vimeo.css"
],
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const urlPattern =

/**
* Extract a YouTube ID from a URL if it matches the pattern.
* @param {string} url URL to test
* @returns {string|undefined} A YouTube video ID or undefined if none matched
* @param url URL to test
* @returns A YouTube video ID or undefined if none matched
*/
export default function matcher(url) {
export default function matcher(url: string): string | undefined {
const match = url.match(urlPattern);
return match?.[3];
}
8 changes: 4 additions & 4 deletions packages/astro-embed-youtube/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"description": "Component to easily embed YouTube videos on your Astro site",
"type": "module",
"exports": {
".": "./index.js",
"./matcher": "./matcher.js"
".": "./index.ts",
"./matcher": "./matcher.ts"
},
"files": [
"index.js",
"matcher.js",
"index.ts",
"matcher.ts",
"YouTube.astro"
],
"repository": {
Expand Down
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 744ddc8

Please sign in to comment.