Skip to content

Commit

Permalink
document all the plugins (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
SeriousBug authored Jun 30, 2024
1 parent f9d1bc3 commit abc4811
Show file tree
Hide file tree
Showing 14 changed files with 391 additions and 57 deletions.
2 changes: 1 addition & 1 deletion packages/date/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microfern/date",
"version": "0.2.0",
"version": "0.2.1",
"description": "Date manipulation plugins for microfern string templating engine.",
"license": "MIT",
"type": "module",
Expand Down
38 changes: 26 additions & 12 deletions packages/date/src/date.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { Plugin, HigherOrderPlugin } from "microfern";
import * as date from "date-fns";
import { fromUnixTime } from "date-fns/fromUnixTime";
import { parseISO } from "date-fns/parseISO";
import { getUnixTime } from "date-fns/getUnixTime";
import { formatISO } from "date-fns/formatISO";
import { add } from "date-fns/add";
import { format } from "date-fns/format";
import type { Duration } from "date-fns";

function parseInt(input: string): number {
const parsed = Number.parseInt(input, 10);
Expand All @@ -9,7 +15,7 @@ function parseInt(input: string): number {
return parsed;
}

type DateUnit = keyof date.Duration;
type DateUnit = keyof Duration;
const dateUnits: ReadonlySet<string> = new Set<string>([
"years",
"months",
Expand All @@ -33,22 +39,30 @@ function parseDateUnit(input: string): DateUnit {

function parseDateInput(rawInput: string): Date {
if (/^\d+$/.test(rawInput)) {
return date.fromUnixTime(parseInt(rawInput));
return fromUnixTime(parseInt(rawInput));
}
const input = date.parseISO(rawInput);
if (input.toString() === "Invalid Date") {
throw new Error(`Invalid date: ${rawInput}`);

let input = parseISO(rawInput);
if (!isNaN(input.getTime())) {
return input;
}
return input;

input = new Date(rawInput);
if (!isNaN(input.getTime())) {
return input;
}

throw new Error(`Invalid date: ${rawInput}`);
}

export const toUnixTimestamp: Plugin = (rawInput: string) => {
const input = parseDateInput(rawInput);
return date.getUnixTime(input).toString();
return getUnixTime(input).toString();
};

export const toISODateTime: Plugin = (rawInput: string) => {
const input = parseDateInput(rawInput);
return date.formatISO(input);
return formatISO(input);
};

export const addTime: HigherOrderPlugin = (
Expand All @@ -59,7 +73,7 @@ export const addTime: HigherOrderPlugin = (
const amount = parseInt(rawAmount);
const input = parseDateInput(rawInput);
const unit = parseDateUnit(rawUnit);
return date.formatISO(date.add(input, { [unit]: amount }));
return formatISO(add(input, { [unit]: amount }));
};
};

Expand All @@ -72,10 +86,10 @@ export const subtractTime: HigherOrderPlugin = (
};

export const formatDateTime: HigherOrderPlugin =
(...format: string[]) =>
(...formatString: string[]) =>
(rawInput: string) => {
const input = parseDateInput(rawInput);
return date.format(input, format.join(" "));
return format(input, formatString.join(" "));
};

export const DATE_PLUGINS = {
Expand Down
15 changes: 15 additions & 0 deletions packages/date/src/doc.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { format } from "microfern";
import { DATE_PLUGINS } from "./date";
import { describe, expect, test } from "vitest";

describe("date.md", () => {
test("basic", () => {
expect(
format(
"I'll eat another apple on {{ date | toUnixTimestamp }}",
{ date: new Date().toString() },
{ plugins: { ...DATE_PLUGINS } }
)
).toMatch(/I'll eat another apple on \d+/);
});
});
39 changes: 7 additions & 32 deletions packages/date/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,23 @@
/// <reference types="vitest" />
import { resolve } from "node:path";
import { isAbsolute, resolve } from "path";
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
import { InlineConfig } from "vitest";

const testRuntime = process.env.TEST_RUNTIME || "node";
let testConfig: InlineConfig = {};

if (testRuntime === "node") {
testConfig = {
environment: "node",
coverage: {
provider: "v8",
},
};
}
// Not edge the browser, serverless edge computing runtime
if (testRuntime === "edge") {
testConfig = {
environment: "edge-runtime",
};
}

if (["chrome", "firefox", "safari"].includes(testRuntime)) {
testConfig = {
browser: {
provider: "webdriverio",
name: testRuntime,
enabled: true,
},
};
}

export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, "src/date.ts"),
formats: ["es", "cjs"],
},
rollupOptions: {
// Externalize deps, otherwise Vite will bundle them into this library,
// and callers will get duplicate deps if they already have any of these
// deps installed.
external: (id) => !(isAbsolute(id) || id.startsWith(".")),
},
},
plugins: [
dts({
rollupTypes: true,
}),
],
test: testConfig,
});
25 changes: 24 additions & 1 deletion site/docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ You can import the package through a CDN if you prefer. This is [supported in mo
but not in Node.js (as of Node 22, it's behind the `--experimental-network-imports` flag).

```js
import { format } from "https://unpkg.com/microfern@0.1.1/dist/microfern.js";
import { format } from "https://unpkg.com/microfern@0.2.1/dist/microfern.js";
```

## Usage
Expand Down Expand Up @@ -81,3 +81,26 @@ format(
{ plugins: DEFAULT_PLUGINS }
);
```

### Always Using the Same Plugins or Variables

If you have a set of plugins or variables that you'd like to always use,
you can do so by wrapping the `format` function. For example:

```ts
import { format as baseFormat } from "microfern";
import { DEFAULT_PLUGINS } from "microfern/plugins";

const variables = {
fruit: "cherry",
vegetable: "okra",
} as const;

export function format(template: string) {
return baseFormat(template, variables, { plugins: DEFAULT_PLUGINS });
}
```

You'll then be able to import and use your own wrapped function whenever you
need to format something, and have all the variables and plugins will always be
available.
13 changes: 13 additions & 0 deletions site/docs/plugins/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"position": 3,
"label": "Plugins",
"collapsible": true,
"collapsed": false,
"link": {
"type": "generated-index",
"title": "Tutorial overview"
},
"customProps": {
"description": "This description can be used in the swizzled DocCard"
}
}
162 changes: 162 additions & 0 deletions site/docs/plugins/built-in.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
sidebar_position: 1
description: Built-in plugins that come with microfern.
---

import { TemplateRepl } from "../../src/components/TemplateRepl";

# Built-in Plugins

These plugins are built into microfern. You do have to import and pass them into
`format` explicitly, but they are included in the standard install.
Check [this example](http://localhost:3000/docs/intro#plugins) to see how to do that.

## List of Plugins

### uppercase
This plugin converts all characters in the input text to uppercase.

<TemplateRepl
template="Chef's secret: {{ fruit | uppercase }} is the key to a good smoothie!"
variables={{ fruit: "banana" }}
/>

### lowercase
This plugin converts all characters in the input text to lowercase.

<TemplateRepl
template="Whisper it: i love {{ veggie | lowercase }} in my salad!"
variables={{ veggie: "BROCCOLI" }}
/>

### capitalize
This plugin capitalizes the first character of the input text.

<TemplateRepl
template="Once upon a thyme, there was an {{ herb | capitalize }}..."
variables={{ herb: "italian parsley" }}
/>

### titleCase
This plugin converts the input text to title case, capitalizing the first letter of each word.

<TemplateRepl
template="Breaking News: {{ headline | titleCase }}"
variables={{ headline: "local man finds tomato that looks like elvis" }}
/>

### snakeCase
This plugin replaces spaces in the input text with underscores.

<TemplateRepl
template="Secret code: {{ message | snakeCase }}"
variables={{ message: "the carrots are listening" }}
/>

### kebabCase
This plugin replaces spaces in the input text with hyphens.

<TemplateRepl
monospaceOutput
template={`<a href="/meals/{{ dish | kebabCase }}">Today's special</a>`}
variables={{ dish: "Eggplant Extravaganza" }}
/>

### camelCase
This plugin converts the input text to camel case, removing spaces and capitalizing the first letter of each word except the first.

<TemplateRepl
monospaceOutput
template="Variable name: {{ fruitSalad | camelCase }}IsYummy"
variables={{ fruitSalad: "tropical fruit bonanza" }}
/>

### pascalCase
This plugin converts the input text to pascal case, removing spaces and capitalizing the first letter of each word.

<TemplateRepl
monospaceOutput
template="Class name: {{ veggieDish | pascalCase }}Recipe"
variables={{ veggieDish: "turnip the beet salad" }}
/>

### trim
This plugin removes whitespace from both ends of the input text.

<TemplateRepl
template="Remember to trim the {{ ingredient | trim }} before cooking!"
variables={{ ingredient: " string beans " }}
/>

### trimStart
This plugin removes whitespace from the beginning of the input text.

<TemplateRepl
template="[{{ shape | trimStart }}] shaped rhubarb"
variables={{ shape: " fish " }}
/>

### trimEnd
This plugin removes whitespace from the end of the input text.

<TemplateRepl
template="[{{ shape | trimEnd }}] shaped rhubarb"
variables={{ shape: " rhubarb " }}
/>

### urlEscape
This plugin URL-encodes the input text.

<TemplateRepl
monospaceOutput
template={`<a href="/search/?q={{ search | urlEscape }}> Search </a>`}
variables={{ search: "how to tuna fish" }}
/>

### urlUnescape
This plugin URL-decodes the input text.

<TemplateRepl
template="You searched: {{ query | urlUnescape }}"
variables={{ query: "Are%20tomatoes%20secretly%20fruit%20spies%3F" }}
/>

### reverse
This plugin reverses the characters in the input text.

<TemplateRepl
template={`"I say, {{ phrase }}! That didn't work? Then {{ phrase | reverse }}!"`}
variables={{ phrase: "open sesame" }}
/>

### escapeHtml
This plugin escapes special HTML characters in the input text.

<TemplateRepl
template="shop for {{ html | escapeHtml }}"
variables={{ html: "my favorite apple variety is the <script>bobby tables apple</script>" }}
/>

### unescapeHtml
This plugin unescapes special HTML characters in the input text.

<TemplateRepl
template="Joke of the day: {{ html | unescapeHtml }}"
variables={{ html: "Can I get some peas &amp; quiet?!" }}
/>

### stripHtml
This plugin removes all HTML tags from the input text.

<TemplateRepl
template="Foiled again, {{ html | stripHtml }}!"
variables={{ html: "<script>baked potato</script>" }}
/>

### truncate
This plugin truncates the input text to the specified length. It's a higher-order plugin that takes a length parameter, which is required.

<TemplateRepl
template="The Amazing {{ veggie | truncate 21 }}"
variables={{ veggie: "Purple Sprouting Broccoli" }}
/>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
sidebar_position: 3
description: Create your own plugins that manipulate variables to use in templates.
---

# Custom Plugins
Expand Down
Loading

0 comments on commit abc4811

Please sign in to comment.