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

feat(og-gen): Adds package and vite plugin for dynamic og generation #10439

Merged
merged 14 commits into from
Apr 12, 2024
Merged
1 change: 1 addition & 0 deletions .changesets/10439.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- feat(og-gen): Adds package and vite plugin for dynamic og generation (#10439) by @dac09
1 change: 1 addition & 0 deletions packages/ogimage-gen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!src/__fixtures__/**/dist
3 changes: 3 additions & 0 deletions packages/ogimage-gen/build.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { build } from '@redwoodjs/framework-tools'

await build()
3 changes: 3 additions & 0 deletions packages/ogimage-gen/cjsWrappers/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* eslint-env node */

module.exports = require('../dist/vite-plugin-ogimage-gen.js').default
50 changes: 50 additions & 0 deletions packages/ogimage-gen/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@redwoodjs/ogimage-gen",
"version": "7.0.0",
"repository": {
"type": "git",
"url": "https://github.com/redwoodjs/redwood.git",
"directory": "packages/ogimage-gen"
},
"license": "MIT",
"exports": {
".": {
"default": "./dist/index.js"
},
"./plugin": {
"import": "./dist/vite-plugin-ogimage-gen.js",
"default": "./cjsWrappers/plugin.js"
}
},
"files": [
"dist",
"cjsWrappers"

],
"scripts": {
"build": "tsx ./build.mts && yarn build:types",
"build:pack": "yarn pack -o redwoodjs-ogimage-gen.tgz",
"build:types": "tsc --build --verbose",
"prepublishOnly": "NODE_ENV=production yarn build",
"test": "vitest run",
"test:watch": "vitest watch"
},
"dependencies": {
"@redwoodjs/internal": "workspace:*",
"@redwoodjs/project-config": "workspace:*",
"@redwoodjs/router": "workspace:*",
"@redwoodjs/vite": "workspace:*",
"fast-glob": "3.3.2",
"react": "18.3.0-canary-a870b2d54-20240314",
"react-dom": "18.3.0-canary-a870b2d54-20240314"
},
"devDependencies": {
"@redwoodjs/framework-tools": "workspace:*",
"ts-toolbelt": "9.6.0",
"tsx": "4.7.1",
"typescript": "5.4.3",
"vite": "5.2.8",
"vitest": "1.4.0"
},
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1"
}
86 changes: 86 additions & 0 deletions packages/ogimage-gen/src/vite-plugin-ogimage-gen.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { vol, fs as memfs } from 'memfs'
import { describe, expect, test, vi, beforeAll, afterAll } from 'vitest'

import { ensurePosixPath } from '@redwoodjs/project-config'

import vitePluginOgGen from './vite-plugin-ogimage-gen'

// Memfs mocks the redwood project-config stuff
vi.mock('fs', () => ({ ...memfs, default: { ...memfs } }))
vi.mock('node:fs', () => ({ ...memfs, default: { ...memfs } }))

/**
* + "ogImage/pages\\About\\AboutPage.jpg": "/redwood-app/web/src/pages/About/AboutPage.jpg.jsx",
+ "ogImage/pages\\Contact\\ContactPage.png": "/redwood-app/web/src/pages/Contact/ContactPage.png.jsx",
+ "ogGen\\pages\\Posts\\PostsPage\\PostsPage.png": "/redwood-app/web/src/pages/Posts/PostsPage/PostsPage.png.tsx",
*/

describe('vitePluginOgGen', () => {
let original_RWJS_CWD

beforeAll(() => {
original_RWJS_CWD = process.env.RWJS_CWD
process.env.RWJS_CWD = '/redwood-app'
// Mock the file system using memfs
vol.fromJSON(
{
'redwood.toml': '',
'web/src/pages/Posts/PostsPage/PostsPage.png.tsx': 'PostsOG',
'web/src/pages/About/AboutPage.jpg.jsx': 'AboutOG',
'web/src/pages/Contact/ContactPage.png.jsx': 'ContactOG',
},
'/redwood-app',
)
})

afterAll(() => {
process.env.RWJS_CWD = original_RWJS_CWD
})

test('should generate rollup inputs for all OG components', async () => {
// Type cast so TS doesn't complain calling config below
// because config can be of many types!
const plugin = (await vitePluginOgGen()) as {
config: (...args: any) => any
}

const rollupInputs = plugin.config().build?.rollupOptions?.input

const inputKeys = Object.keys(rollupInputs)

expect(inputKeys).toEqual(
expect.arrayContaining([
'ogImage/pages/Posts/PostsPage/PostsPage.png',
'ogImage/pages/About/AboutPage.jpg',
'ogImage/pages/Contact/ContactPage.png',
]),
)

// For windows, we do the conversion before the test
expect(
ensurePosixPath(
rollupInputs['ogImage/pages/Posts/PostsPage/PostsPage.png'],
),
).toMatch('/redwood-app/web/src/pages/Posts/PostsPage/PostsPage.png.tsx')

expect(
ensurePosixPath(rollupInputs['ogImage/pages/About/AboutPage.jpg']),
).toMatch('/redwood-app/web/src/pages/About/AboutPage.jpg.jsx')

expect(
ensurePosixPath(rollupInputs['ogImage/pages/Contact/ContactPage.png']),
).toMatch('/redwood-app/web/src/pages/Contact/ContactPage.png.jsx')
})

test('returns the correct Vite plugin object', async () => {
const expectedPlugin = {
name: 'rw-vite-plugin-ogimage-gen',
apply: 'build', // Important!
config: expect.any(Function),
}

const plugin = await vitePluginOgGen()

expect(plugin).toEqual(expectedPlugin)
})
})
56 changes: 56 additions & 0 deletions packages/ogimage-gen/src/vite-plugin-ogimage-gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import fs from 'fs'
import path from 'node:path'

import fg from 'fast-glob'
import type { O } from 'ts-toolbelt'
import type { Plugin as VitePlugin } from 'vite'

import { ensurePosixPath, getPaths } from '@redwoodjs/project-config'

type ConfigPlugin = O.Required<VitePlugin, 'config'>

/**
* This plugin updates the rollup inputs to include all OG components.
*
* Internally, Redwood's vite settings will merge this with the default vite config, and any user config
*/
function vitePluginOgImageGen(): ConfigPlugin {
const rwPaths = getPaths()

const allOgComponents = fg.sync('pages/**/*.{png,jpg}.{jsx,tsx}', {
cwd: rwPaths.web.src,
absolute: true,
// @MARK This allows us to mock the fs module in tests
fs,
})

// Generates the rollup inputs for all OG components, in their subpaths
// The actual filename doesn't have to match the Page name, just has to be next to the Page
// e.g. { 'ogGen/pages/Posts/PostsPage': '/../pages/Posts/PostsPage/PostsPage.og.png.tsx'}

const ogComponentInput: Record<string, string> = {}
allOgComponents.forEach((ogComponentPath) => {
const pathKey = path
.relative(rwPaths.web.src, ogComponentPath)
.replace(/\.[jt]sx?$/, '')

// The rollup input takes '/' to create folders.
ogComponentInput[`ogImage/${ensurePosixPath(pathKey)}`] = ogComponentPath
})

return {
name: 'rw-vite-plugin-ogimage-gen',
apply: 'build', // We only need to update rollup inputs for build
config: () => {
return {
build: {
rollupOptions: {
input: ogComponentInput,
},
},
}
},
}
}

export default vitePluginOgImageGen
24 changes: 24 additions & 0 deletions packages/ogimage-gen/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"extends": "../../tsconfig.compilerOption.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
},
"include": [
"src"
],
"references": [
{
"path": "../project-config"
},
{
"path": "../vite"
},
{
"path": "../router"
},
{
"path": "../internal"
}
]
}
22 changes: 21 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8563,6 +8563,26 @@ __metadata:
languageName: unknown
linkType: soft

"@redwoodjs/ogimage-gen@workspace:packages/ogimage-gen":
version: 0.0.0-use.local
resolution: "@redwoodjs/ogimage-gen@workspace:packages/ogimage-gen"
dependencies:
"@redwoodjs/framework-tools": "workspace:*"
"@redwoodjs/internal": "workspace:*"
"@redwoodjs/project-config": "workspace:*"
"@redwoodjs/router": "workspace:*"
"@redwoodjs/vite": "workspace:*"
fast-glob: "npm:3.3.2"
react: "npm:18.3.0-canary-a870b2d54-20240314"
react-dom: "npm:18.3.0-canary-a870b2d54-20240314"
ts-toolbelt: "npm:9.6.0"
tsx: "npm:4.7.1"
typescript: "npm:5.4.3"
vite: "npm:5.2.8"
vitest: "npm:1.4.0"
languageName: unknown
linkType: soft

"@redwoodjs/prerender@workspace:*, @redwoodjs/prerender@workspace:packages/prerender":
version: 0.0.0-use.local
resolution: "@redwoodjs/prerender@workspace:packages/prerender"
Expand Down Expand Up @@ -8790,7 +8810,7 @@ __metadata:
languageName: unknown
linkType: soft

"@redwoodjs/vite@workspace:packages/vite":
"@redwoodjs/vite@workspace:*, @redwoodjs/vite@workspace:packages/vite":
version: 0.0.0-use.local
resolution: "@redwoodjs/vite@workspace:packages/vite"
dependencies:
Expand Down
Loading